summaryrefslogtreecommitdiff
path: root/MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Output.c
blob: 93cc52eb57f1687863fc1c4cd3412b8701bc38ba (plain)
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
465
466
467
/** @file
  Transmit the IP4 packet.
  
Copyright (c) 2005 - 2009, Intel Corporation. All rights reserved.<BR>
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 ((UINT16) 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;
  }

  //
  // Before IPsec process, prepared the IP head.
  //
  HeadLen        = sizeof (IP4_HEAD) + ((OptLen + 3) & (~0x03));
  Head->HeadLen  = (UINT8) HeadLen >> 2;
  Head->Id       = mIp4Id++;
  Head->Ver      = 4;
  
  //
  // Call IPsec process.
  //
  Status = Ip4IpSecProcessPacket (
             IpSb, 
             &Head, 
             &Packet, 
             &Option, 
             &OptLen, 
             EfiIPsecOutBound,
             Context
             );

  if (EFI_ERROR(Status)) {
    return Status;
  }

  //
  // 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);
    }
  }

  //
  // OK, selected the source and route, fragment the packet then send
  // them. Tag each fragment other than the first one as spawn from it.
  //
  Mtu = IpSb->MaxPacketSize + sizeof (IP4_HEAD);  

  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);
}