1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
|
/** @file
Transmit the IP4 packet.
Copyright (c) 2005 - 2009, Intel Corporation.<BR>
All rights reserved. This program and the accompanying materials
are licensed and made available under the terms and conditions of the BSD License
which accompanies this distribution. The full text of the license may be found at
http://opensource.org/licenses/bsd-license.php
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
**/
#include "Ip4Impl.h"
UINT16 mIp4Id;
/**
Prepend an IP4 head to the Packet. It will copy the options and
build the IP4 header fields. Used for IP4 fragmentation.
@param Packet The packet to prepend IP4 header to
@param Head The caller supplied header. The caller should set
the following header fields: Tos, TotalLen, Id,
Fragment, Ttl, Protocol, Src and Dst. All the fields
are in host byte order. This function will fill in
the Ver, HeadLen, and checksum.
@param Option The orginal IP4 option to copy from
@param OptLen The length of the IP4 option
@retval EFI_BAD_BUFFER_SIZE There is no enought room in the head space of
Packet.
@retval EFI_SUCCESS The IP4 header is successfully added to the packet.
**/
EFI_STATUS
Ip4PrependHead (
IN OUT NET_BUF *Packet,
IN IP4_HEAD *Head,
IN UINT8 *Option,
IN UINT32 OptLen
)
{
UINT32 HeadLen;
UINT32 Len;
IP4_HEAD *PacketHead;
BOOLEAN FirstFragment;
//
// Prepend the options: first get the option length, then copy it over.
//
HeadLen = 0;
FirstFragment = IP4_FIRST_FRAGMENT (Head->Fragment);
Ip4CopyOption (Option, OptLen, FirstFragment, NULL, &Len);
HeadLen = IP4_MIN_HEADLEN + Len;
ASSERT (((Len % 4) == 0) && (HeadLen <= IP4_MAX_HEADLEN));
PacketHead = (IP4_HEAD *) NetbufAllocSpace (Packet, HeadLen, NET_BUF_HEAD);
if (PacketHead == NULL) {
return EFI_BAD_BUFFER_SIZE;
}
Ip4CopyOption (Option, OptLen, FirstFragment, (UINT8 *) (PacketHead + 1), &Len);
//
// Set the head up, convert the host byte order to network byte order
//
PacketHead->Ver = 4;
PacketHead->HeadLen = (UINT8) (HeadLen >> 2);
PacketHead->Tos = Head->Tos;
PacketHead->TotalLen = HTONS (Packet->TotalSize);
PacketHead->Id = HTONS (Head->Id);
PacketHead->Fragment = HTONS (Head->Fragment);
PacketHead->Checksum = 0;
PacketHead->Ttl = Head->Ttl;
PacketHead->Protocol = Head->Protocol;
PacketHead->Src = HTONL (Head->Src);
PacketHead->Dst = HTONL (Head->Dst);
PacketHead->Checksum = (UINT16) (~NetblockChecksum ((UINT8 *) PacketHead, HeadLen));
Packet->Ip.Ip4 = PacketHead;
return EFI_SUCCESS;
}
/**
Select an interface to send the packet generated in the IP4 driver
itself, that is, not by the requests of IP4 child's consumer. Such
packets include the ICMP echo replies, and other ICMP error packets.
@param[in] IpSb The IP4 service that wants to send the packets.
@param[in] Dst The destination of the packet
@param[in] Src The source of the packet
@return NULL if no proper interface is found, otherwise the interface that
can be used to send the system packet from.
**/
IP4_INTERFACE *
Ip4SelectInterface (
IN IP4_SERVICE *IpSb,
IN IP4_ADDR Dst,
IN IP4_ADDR Src
)
{
IP4_INTERFACE *IpIf;
IP4_INTERFACE *Selected;
LIST_ENTRY *Entry;
//
// Select the interface the Dst is on if one of the connected
// network. Some IP instance may be configured with 0.0.0.0/0,
// don't select that interface now.
//
IpIf = Ip4FindNet (IpSb, Dst);
if ((IpIf != NULL) && (IpIf->Ip != IP4_ALLZERO_ADDRESS)) {
return IpIf;
}
//
// If source is one of the interface address, select it.
//
IpIf = Ip4FindInterface (IpSb, Src);
if ((IpIf != NULL) && (IpIf->Ip != IP4_ALLZERO_ADDRESS)) {
return IpIf;
}
//
// Select a configured interface as the fall back. Always prefer
// an interface with non-zero address.
//
Selected = NULL;
NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) {
IpIf = NET_LIST_USER_STRUCT (Entry, IP4_INTERFACE, Link);
if (IpIf->Configured && ((Selected == NULL) || (Selected->Ip == 0))) {
Selected = IpIf;
}
}
return Selected;
}
/**
The default callback function for system generated packet.
It will free the packet.
@param Ip4Instance The IP4 child that issued the transmission. It most
like is NULL.
@param Packet The packet that transmitted.
@param IoStatus The result of the transmission, succeeded or failed.
@param LinkFlag Not used when transmission. check IP4_FRAME_CALLBACK
for reference.
@param Context The context provided by us
**/
VOID
Ip4SysPacketSent (
IP4_PROTOCOL *Ip4Instance,
NET_BUF *Packet,
EFI_STATUS IoStatus,
UINT32 LinkFlag,
VOID *Context
)
{
NetbufFree (Packet);
}
/**
Transmit an IP4 packet. The packet comes either from the IP4
child's consumer (IpInstance != NULL) or the IP4 driver itself
(IpInstance == NULL). It will route the packet, fragment it,
then transmit all the fragments through some interface.
@param[in] IpSb The IP4 service instance to transmit the packet
@param[in] IpInstance The IP4 child that issues the transmission. It is
NULL if the packet is from the system.
@param[in] Packet The user data to send, excluding the IP header.
@param[in] Head The caller supplied header. The caller should set
the following header fields: Tos, TotalLen, Id, tl,
Fragment, Protocol, Src and Dst. All the fields are
in host byte order. This function will fill in the
Ver, HeadLen, Fragment, and checksum. The Fragment
only need to include the DF flag. Ip4Output will
compute the MF and offset for you.
@param[in] Option The original option to append to the IP headers
@param[in] OptLen The length of the option
@param[in] GateWay The next hop address to transmit packet to.
255.255.255.255 means broadcast.
@param[in] Callback The callback function to issue when transmission
completed.
@param[in] Context The opaque context for the callback
@retval EFI_NO_MAPPING There is no interface to the destination.
@retval EFI_NOT_FOUND There is no route to the destination
@retval EFI_SUCCESS The packet is successfully transmitted.
@retval Others Failed to transmit the packet.
**/
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
)
{
IP4_INTERFACE *IpIf;
IP4_ROUTE_CACHE_ENTRY *CacheEntry;
IP4_ADDR Dest;
EFI_STATUS Status;
NET_BUF *Fragment;
UINT32 Index;
UINT32 HeadLen;
UINT32 PacketLen;
UINT32 Offset;
UINT32 Mtu;
UINT32 Num;
//
// Select an interface/source for system packet, application
// should select them itself.
//
if (IpInstance == NULL) {
IpIf = Ip4SelectInterface (IpSb, Head->Dst, Head->Src);
} else {
IpIf = IpInstance->Interface;
}
if (IpIf == NULL) {
return EFI_NO_MAPPING;
}
if ((Head->Src == IP4_ALLZERO_ADDRESS) && (IpInstance == NULL)) {
Head->Src = IpIf->Ip;
}
//
// Route the packet unless overrided, that is, GateWay isn't zero.
//
if (GateWay == IP4_ALLZERO_ADDRESS) {
Dest = Head->Dst;
if (IP4_IS_BROADCAST (Ip4GetNetCast (Dest, IpIf)) || (Dest == IP4_ALLONE_ADDRESS)) {
//
// Set the gateway to local broadcast if the Dest is
// the broadcast address for the connected network or
// it is local broadcast.
//
GateWay = IP4_ALLONE_ADDRESS;
} else if (IP4_IS_MULTICAST (Dest)) {
//
// Set the gateway to the destination if it is an multicast
// address. The IP4_INTERFACE won't consult ARP to send local
// broadcast and multicast.
//
GateWay = Head->Dst;
} else {
//
// Consult the route table to route the packet
//
if (IpInstance == NULL) {
CacheEntry = Ip4Route (IpSb->DefaultRouteTable, Head->Dst, Head->Src);
} else {
CacheEntry = Ip4Route (IpInstance->RouteTable, Head->Dst, Head->Src);
}
if (CacheEntry == NULL) {
return EFI_NOT_FOUND;
}
GateWay = CacheEntry->NextHop;
Ip4FreeRouteCacheEntry (CacheEntry);
}
}
//
// TODO: currently Option/OptLen are not included into encryption scope.
//
Status = Ip4IpSecProcessPacket (
IpSb,
Head,
&Packet,
Option,
OptLen,
EfiIPsecOutBound,
Context
);
if (EFI_ERROR(Status)) {
return Status;
}
//
// OK, selected the source and route, fragment the packet then send
// them. Tag each fragment other than the first one as spawn from it.
//
// IPsec payload has been appended, so use IpSb->SnpMode.MaxPacketSize here.
//
Mtu = IpSb->SnpMode.MaxPacketSize;
HeadLen = sizeof (IP4_HEAD) + ((OptLen + 3) & (~0x03));
Head->Id = mIp4Id++;
if (Packet->TotalSize + HeadLen > Mtu) {
//
// Packet is fragmented from the tail to the head, that is, the
// first frame sent is the last fragment of the packet. The first
// fragment is NOT sent in this loop. First compute how many
// fragments there are.
//
Mtu = (Mtu - HeadLen) & (~0x07);
Num = (Packet->TotalSize + Mtu - 1) / Mtu;
//
// Initialize the packet length and Offset. Other than the last
// fragment, the packet length equals to MTU. The offset is always
// aligned to MTU.
//
PacketLen = Packet->TotalSize - (Num - 1) * Mtu;
Offset = Mtu * (Num - 1);
for (Index = 0; Index < Num - 1; Index++, Offset -= Mtu) {
Fragment = NetbufGetFragment (Packet, Offset, PacketLen, IP4_MAX_HEADLEN);
if (Fragment == NULL) {
Status = EFI_OUT_OF_RESOURCES;
goto ON_ERROR;
}
//
// Update the header's fragment. The caller fills the IP4 header
// fields that are required by Ip4PrependHead except the fragment.
//
Head->Fragment = IP4_HEAD_FRAGMENT_FIELD (FALSE, (Index != 0), Offset);
Ip4PrependHead (Fragment, Head, Option, OptLen);
//
// Transmit the fragments, pass the Packet address as the context.
// So, we can find all the fragments spawned from the Packet by
// compare the NetBuf and Context to the Packet.
//
Status = Ip4SendFrame (
IpIf,
IpInstance,
Fragment,
GateWay,
Ip4SysPacketSent,
Packet
);
if (EFI_ERROR (Status)) {
goto ON_ERROR;
}
PacketLen = Mtu;
}
//
// Trim the already sent data, then adjust the head's fragment field.
//
NetbufTrim (Packet, Packet->TotalSize - Mtu, FALSE);
Head->Fragment = IP4_HEAD_FRAGMENT_FIELD (FALSE, TRUE, 0);
}
//
// Send the first fragment, it is either the orginal packet, or the
// first fragment of a fragmented packet. It seems that there is a subtle
// bug here: what if the caller free the packet in Callback and IpIf (or
// MNP child used by that interface) still holds the fragments and try
// to access the data? The caller can free the packet if it recycles the
// consumer's (such as UDP) data in the Callback. But this can't happen.
// The detailed sequence is:
// 1. for the packets generated by IP4 driver itself:
// The Callback is Ip4SysPacketSent, which is the same as the
// fragments' callback. Ip4SysPacketSent simply calls NetbufFree
// to release its reference to the packet. So, no problem for
// system packets.
//
// 2. for the upper layer's packets (use UDP as an example):
// UDP requests the IP layer to transmit some data which is
// wrapped in an asynchronous token, the token is wrapped
// in IP4_TXTOKEN_WRAP by IP4. IP4 also wrap the user's data
// in a net buffer, which is Packet we get here. IP4_TXTOKEN_WRAP
// is bound with the Packet. It will only be freed when all
// the references to Packet have been released. Upon then, the
// Packet's OnFree callback will release the IP4_TXTOKEN_WRAP,
// and singal the user's recycle event. So, also no problem for
// upper layer's packets.
//
Ip4PrependHead (Packet, Head, Option, OptLen);
Status = Ip4SendFrame (IpIf, IpInstance, Packet, GateWay, Callback, Context);
if (EFI_ERROR (Status)) {
goto ON_ERROR;
}
return EFI_SUCCESS;
ON_ERROR:
Ip4CancelPacket (IpIf, Packet, Status);
return Status;
}
/**
The filter function to find a packet and all its fragments.
The packet's fragments have their Context set to the packet.
@param[in] Frame The frames hold by the low level interface
@param[in] Context Context to the function, which is the packet.
@retval TRUE This is the packet to cancel or its fragments.
@retval FALSE This is unrelated packet.
**/
BOOLEAN
Ip4CancelPacketFragments (
IN IP4_LINK_TX_TOKEN *Frame,
IN VOID *Context
)
{
if ((Frame->Packet == (NET_BUF *) Context) || (Frame->Context == Context)) {
return TRUE;
}
return FALSE;
}
/**
Cancel the Packet and all its fragments.
@param IpIf The interface from which the Packet is sent
@param Packet The Packet to cancel
@param IoStatus The status returns to the sender.
**/
VOID
Ip4CancelPacket (
IN IP4_INTERFACE *IpIf,
IN NET_BUF *Packet,
IN EFI_STATUS IoStatus
)
{
Ip4CancelFrames (IpIf, IoStatus, Ip4CancelPacketFragments, Packet);
}
|