summaryrefslogtreecommitdiffstats
path: root/NetworkPkg/Ip4Dxe/Ip4Icmp.c
blob: 833ffa74ff0dd25199cc6076f67de65bf21adf0b (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
/** @file

Copyright (c) 2005 - 2018, Intel Corporation. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent

**/

#include "Ip4Impl.h"

IP4_ICMP_CLASS
mIcmpClass[] = {
  {ICMP_ECHO_REPLY,         ICMP_QUERY_MESSAGE  },
  {1,                       ICMP_INVALID_MESSAGE},
  {2,                       ICMP_INVALID_MESSAGE},
  {ICMP_DEST_UNREACHABLE,   ICMP_ERROR_MESSAGE  },
  {ICMP_SOURCE_QUENCH,      ICMP_ERROR_MESSAGE  },
  {ICMP_REDIRECT,           ICMP_ERROR_MESSAGE  },
  {6,                       ICMP_INVALID_MESSAGE},
  {7,                       ICMP_INVALID_MESSAGE},
  {ICMP_ECHO_REQUEST,       ICMP_QUERY_MESSAGE  },
  {9,                       ICMP_INVALID_MESSAGE},
  {10,                      ICMP_INVALID_MESSAGE},
  {ICMP_TIME_EXCEEDED,      ICMP_ERROR_MESSAGE  },
  {ICMP_PARAMETER_PROBLEM,  ICMP_ERROR_MESSAGE  },
  {ICMP_TIMESTAMP ,         ICMP_QUERY_MESSAGE  },
  {14,                      ICMP_INVALID_MESSAGE},
  {ICMP_INFO_REQUEST ,      ICMP_QUERY_MESSAGE  },
  {ICMP_INFO_REPLY ,        ICMP_QUERY_MESSAGE  },
};

EFI_IP4_ICMP_TYPE
mIp4SupportedIcmp[23] = {
  {ICMP_ECHO_REPLY,        ICMP_DEFAULT_CODE        },

  {ICMP_DEST_UNREACHABLE,  ICMP_NET_UNREACHABLE     },
  {ICMP_DEST_UNREACHABLE,  ICMP_HOST_UNREACHABLE    },
  {ICMP_DEST_UNREACHABLE,  ICMP_PROTO_UNREACHABLE   },
  {ICMP_DEST_UNREACHABLE,  ICMP_PORT_UNREACHABLE    },
  {ICMP_DEST_UNREACHABLE,  ICMP_FRAGMENT_FAILED     },
  {ICMP_DEST_UNREACHABLE,  ICMP_SOURCEROUTE_FAILED  },
  {ICMP_DEST_UNREACHABLE,  ICMP_NET_UNKNOWN         },
  {ICMP_DEST_UNREACHABLE,  ICMP_HOST_UNKNOWN        },
  {ICMP_DEST_UNREACHABLE,  ICMP_SOURCE_ISOLATED     },
  {ICMP_DEST_UNREACHABLE,  ICMP_NET_PROHIBITED      },
  {ICMP_DEST_UNREACHABLE,  ICMP_HOST_PROHIBITED     },
  {ICMP_DEST_UNREACHABLE,  ICMP_NET_UNREACHABLE_TOS },
  {ICMP_DEST_UNREACHABLE,  ICMP_HOST_UNREACHABLE_TOS},

  {ICMP_SOURCE_QUENCH,     ICMP_DEFAULT_CODE        },

  {ICMP_REDIRECT,          ICMP_NET_REDIRECT        },
  {ICMP_REDIRECT,          ICMP_HOST_REDIRECT       },
  {ICMP_REDIRECT,          ICMP_NET_TOS_REDIRECT    },
  {ICMP_REDIRECT,          ICMP_HOST_TOS_REDIRECT   },

  {ICMP_ECHO_REQUEST,      ICMP_DEFAULT_CODE        },

  {ICMP_TIME_EXCEEDED,     ICMP_TIMEOUT_IN_TRANSIT  },
  {ICMP_TIME_EXCEEDED,     ICMP_TIMEOUT_REASSEMBLE  },

  {ICMP_PARAMETER_PROBLEM, ICMP_DEFAULT_CODE        },
};



/**
  Process the ICMP redirect. Find the instance then update
  its route cache.

  All kinds of redirect is treated as host redirect as
  specified by RFC1122 3.3.1.2:
  "Since the subnet mask appropriate to the destination
  address is generally not known, a Network Redirect
  message SHOULD be treated identically to a Host Redirect
  message;"

  @param[in]  IpSb               The IP4 service binding instance that received
                                 the packet.
  @param[in]  Head               The IP head of the received ICMPpacket.
  @param[in]  Packet             The content of the ICMP redirect packet with IP
                                 head removed.
  @param[in]  Icmp               The buffer to store the ICMP error message if
                                 something is wrong.

  @retval EFI_INVALID_PARAMETER  The parameter is invalid
  @retval EFI_SUCCESS            Successfully updated the route caches

**/
EFI_STATUS
Ip4ProcessIcmpRedirect (
  IN IP4_SERVICE            *IpSb,
  IN IP4_HEAD               *Head,
  IN NET_BUF                *Packet,
  IN IP4_ICMP_ERROR_HEAD    *Icmp
  )
{
  LIST_ENTRY                *Entry;
  IP4_PROTOCOL              *Ip4Instance;
  IP4_ROUTE_CACHE_ENTRY     *CacheEntry;
  IP4_INTERFACE             *IpIf;
  IP4_ADDR                  Gateway;
  IP4_ADDR                  Src;
  IP4_ADDR                  Dst;

  //
  // Find the interface whose IP address is the source of the
  // orgianl IP packet.
  //
  IpIf    = Ip4FindInterface (IpSb, NTOHL (Icmp->IpHead.Src));
  Gateway = NTOHL (Icmp->Fourth);

  //
  // discard the packet if the new gateway address it specifies
  // is not on the same connected net through which the Redirect
  // arrived. (RFC1122 3.2.2.2).
  //
  if ((IpIf == NULL) || !IP4_NET_EQUAL (Gateway, IpIf->Ip, IpIf->SubnetMask)) {
    NetbufFree (Packet);
    return EFI_INVALID_PARAMETER;
  }

  //
  // Update each IP child's route cache on the interface.
  //
  NET_LIST_FOR_EACH (Entry, &IpIf->IpInstances) {
    Ip4Instance = NET_LIST_USER_STRUCT (Entry, IP4_PROTOCOL, AddrLink);

    if (Ip4Instance->RouteTable == NULL) {
      continue;
    }

    Dst = NTOHL (Icmp->IpHead.Dst);
    Src = NTOHL (Icmp->IpHead.Src);
    CacheEntry = Ip4FindRouteCache (Ip4Instance->RouteTable, Dst, Src);

    //
    // Only update the route cache's gateway if the source of the
    // Redirect is the current first-hop gateway
    //
    if ((CacheEntry != NULL) && (NTOHL (Head->Src) == CacheEntry->NextHop)) {
      CacheEntry->NextHop = Gateway;
    }
  }

  NetbufFree (Packet);
  return EFI_SUCCESS;
}


/**
  Process the ICMP error packet. If it is an ICMP redirect packet,
  update call Ip4ProcessIcmpRedirect to update the IP instance's
  route cache, otherwise, deliver the packet to upper layer.

  @param[in]  IpSb               The IP4 service that received the packet.
  @param[in]  Head               The IP4 head of the ICMP error packet
  @param[in]  Packet             The content of the ICMP error with IP4 head
                                 removed.

  @retval EFI_SUCCESS            The ICMP error is processed successfully.
  @retval EFI_INVALID_PARAMETER  The packet is invalid
  @retval Others                 Failed to process the packet.

**/
EFI_STATUS
Ip4ProcessIcmpError (
  IN IP4_SERVICE            *IpSb,
  IN IP4_HEAD               *Head,
  IN NET_BUF                *Packet
  )
{
  IP4_ICMP_ERROR_HEAD       Icmp;

  if (Packet->TotalSize < sizeof (Icmp)) {
    NetbufFree (Packet);
    return EFI_INVALID_PARAMETER;
  }

  NetbufCopy (Packet, 0, sizeof (Icmp), (UINT8 *) &Icmp);

  //
  // If it is an ICMP redirect error, update the route cache
  // as RFC1122. Otherwise, demultiplex it to IP instances.
  //
  if (Icmp.Head.Type == ICMP_REDIRECT) {
    return Ip4ProcessIcmpRedirect (IpSb, Head, Packet, &Icmp);
  }

  IP4_GET_CLIP_INFO (Packet)->Status = EFI_ICMP_ERROR;
  return Ip4Demultiplex (IpSb, Head, Packet, NULL, 0);
}


/**
  Replay an ICMP echo request.

  @param[in]  IpSb               The IP4 service that receivd the packet
  @param[in]  Head               The IP4 head of the ICMP error packet
  @param[in]  Packet             The content of the ICMP error with IP4 head
                                 removed.

  @retval EFI_OUT_OF_RESOURCES   Failed to allocate resource.
  @retval EFI_SUCCESS            The ICMP Echo request is successfully answered.
  @retval Others                 Failed to answer the ICMP echo request.

**/
EFI_STATUS
Ip4IcmpReplyEcho (
  IN IP4_SERVICE            *IpSb,
  IN IP4_HEAD               *Head,
  IN NET_BUF                *Packet
  )
{
  IP4_ICMP_QUERY_HEAD       *Icmp;
  NET_BUF                   *Data;
  EFI_STATUS                Status;
  IP4_HEAD                  ReplyHead;

  //
  // make a copy the packet, it is really a bad idea to
  // send the MNP's buffer back to MNP.
  //
  Data = NetbufDuplicate (Packet, NULL, IP4_MAX_HEADLEN);

  if (Data == NULL) {
    Status = EFI_OUT_OF_RESOURCES;
    goto ON_EXIT;
  }

  //
  // Change the ICMP type to echo reply, exchange the source
  // and destination, then send it. The source is updated to
  // use specific destination. See RFC1122. SRR/RR option
  // update is omitted.
  //
  Icmp                = (IP4_ICMP_QUERY_HEAD *) NetbufGetByte (Data, 0, NULL);
  ASSERT (Icmp != NULL);
  Icmp->Head.Type     = ICMP_ECHO_REPLY;
  Icmp->Head.Checksum = 0;
  Icmp->Head.Checksum = (UINT16) (~NetblockChecksum ((UINT8 *) Icmp, Data->TotalSize));

  ReplyHead.Tos       = 0;
  ReplyHead.Fragment  = 0;
  ReplyHead.Ttl       = 64;
  ReplyHead.Protocol  = EFI_IP_PROTO_ICMP;
  ReplyHead.Src       = 0;

  //
  // Ip4Output will select a source for us
  //
  ReplyHead.Dst = Head->Src;

  Status = Ip4Output (
             IpSb,
             NULL,
             Data,
             &ReplyHead,
             NULL,
             0,
             IP4_ALLZERO_ADDRESS,
             Ip4SysPacketSent,
             NULL
             );
  if (EFI_ERROR (Status)) {
    NetbufFree (Data);
  }

ON_EXIT:
  NetbufFree (Packet);
  return Status;
}


/**
  Process the ICMP query message. If it is an ICMP echo
  request, answer it. Otherwise deliver it to upper layer.

  @param[in]  IpSb               The IP4 service that receivd the packet
  @param[in]  Head               The IP4 head of the ICMP query packet
  @param[in]  Packet             The content of the ICMP query with IP4 head
                                 removed.

  @retval EFI_INVALID_PARAMETER  The packet is invalid
  @retval EFI_SUCCESS            The ICMP query message is processed
  @retval Others                 Failed to process ICMP query.

**/
EFI_STATUS
Ip4ProcessIcmpQuery (
  IN IP4_SERVICE            *IpSb,
  IN IP4_HEAD               *Head,
  IN NET_BUF                *Packet
  )
{
  IP4_ICMP_QUERY_HEAD       Icmp;

  if (Packet->TotalSize < sizeof (Icmp)) {
    NetbufFree (Packet);
    return EFI_INVALID_PARAMETER;
  }

  NetbufCopy (Packet, 0, sizeof (Icmp), (UINT8 *) &Icmp);

  if (Icmp.Head.Type == ICMP_ECHO_REQUEST) {
    return Ip4IcmpReplyEcho (IpSb, Head, Packet);
  }

  return Ip4Demultiplex (IpSb, Head, Packet, NULL, 0);
}


/**
  Handle the ICMP packet. First validate the message format,
  then according to the message types, process it as query or
  error packet.

  @param[in]  IpSb               The IP4 service that receivd the packet.
  @param[in]  Head               The IP4 head of the ICMP query packet.
  @param[in]  Packet             The content of the ICMP query with IP4 head
                                 removed.

  @retval EFI_INVALID_PARAMETER  The packet is malformatted.
  @retval EFI_SUCCESS            The ICMP message is successfully processed.
  @retval Others                 Failed to handle ICMP packet.

**/
EFI_STATUS
Ip4IcmpHandle (
  IN IP4_SERVICE            *IpSb,
  IN IP4_HEAD               *Head,
  IN NET_BUF                *Packet
  )
{
  IP4_ICMP_HEAD             Icmp;
  UINT16                    Checksum;

  if (Packet->TotalSize < sizeof (Icmp)) {
    goto DROP;
  }

  NetbufCopy (Packet, 0, sizeof (Icmp), (UINT8 *) &Icmp);

  if (Icmp.Type > ICMP_TYPE_MAX) {
    goto DROP;
  }

  Checksum = (UINT16) (~NetbufChecksum (Packet));
  if ((Icmp.Checksum != 0) && (Checksum != 0)) {
    goto DROP;
  }

  if (mIcmpClass[Icmp.Type].IcmpClass == ICMP_ERROR_MESSAGE) {
    return Ip4ProcessIcmpError (IpSb, Head, Packet);

  } else if (mIcmpClass[Icmp.Type].IcmpClass == ICMP_QUERY_MESSAGE) {
    return Ip4ProcessIcmpQuery (IpSb, Head, Packet);

  }

DROP:
  NetbufFree (Packet);
  return EFI_INVALID_PARAMETER;
}