1/*
2 * Copyright (C) 2013 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *
16 * icmp.c - convenience functions for translating ICMP and ICMPv6 packets.
17 */
18
19#include <netinet/in.h>
20#include <netinet/ip_icmp.h>
21#include <netinet/icmp6.h>
22#include <linux/icmp.h>
23
24#include "logging.h"
25#include "icmp.h"
26
27/* function: icmp_guess_ttl
28 * Guesses the number of hops a received packet has traversed based on its TTL.
29 * ttl - the ttl of the received packet.
30 */
31uint8_t icmp_guess_ttl(uint8_t ttl) {
32  if (ttl > 128) {
33    return 255 - ttl;
34  } else if (ttl > 64) {
35    return 128 - ttl;
36  } else if (ttl > 32) {
37    return 64 - ttl;
38  } else {
39    return 32 - ttl;
40  }
41}
42
43/* function: is_icmp_error
44 * Determines whether an ICMP type is an error message.
45 * type: the ICMP type
46 */
47int is_icmp_error(uint8_t type) {
48  return type == 3 || type == 11 || type == 12;
49}
50
51/* function: is_icmp6_error
52 * Determines whether an ICMPv6 type is an error message.
53 * type: the ICMPv6 type
54 */
55int is_icmp6_error(uint8_t type) {
56  return type < 128;
57}
58
59/* function: icmp_to_icmp6_type
60 * Maps ICMP types to ICMPv6 types. Partial implementation of RFC 6145, section 4.2.
61 * type - the ICMPv6 type
62 */
63uint8_t icmp_to_icmp6_type(uint8_t type, uint8_t code) {
64  switch (type) {
65    case ICMP_ECHO:
66      return ICMP6_ECHO_REQUEST;
67
68    case ICMP_ECHOREPLY:
69      return ICMP6_ECHO_REPLY;
70
71    case ICMP_TIME_EXCEEDED:
72      return ICMP6_TIME_EXCEEDED;
73
74    case ICMP_DEST_UNREACH:
75      // These two types need special translation which we don't support yet.
76      if (code != ICMP_UNREACH_PROTOCOL && code != ICMP_UNREACH_NEEDFRAG) {
77        return ICMP6_DST_UNREACH;
78      }
79  }
80
81  // We don't understand this ICMP type. Return parameter problem so the caller will bail out.
82  logmsg_dbg(ANDROID_LOG_DEBUG, "icmp_to_icmp6_type: unhandled ICMP type %d", type);
83  return ICMP6_PARAM_PROB;
84}
85
86/* function: icmp_to_icmp6_code
87 * Maps ICMP codes to ICMPv6 codes. Partial implementation of RFC 6145, section 4.2.
88 * type - the ICMP type
89 * code - the ICMP code
90 */
91uint8_t icmp_to_icmp6_code(uint8_t type, uint8_t code) {
92  switch (type) {
93    case ICMP_ECHO:
94    case ICMP_ECHOREPLY:
95      return 0;
96
97    case ICMP_TIME_EXCEEDED:
98      return code;
99
100    case ICMP_DEST_UNREACH:
101      switch (code) {
102        case ICMP_UNREACH_NET:
103        case ICMP_UNREACH_HOST:
104          return ICMP6_DST_UNREACH_NOROUTE;
105
106        case ICMP_UNREACH_PORT:
107          return ICMP6_DST_UNREACH_NOPORT;
108
109        case ICMP_UNREACH_NET_PROHIB:
110        case ICMP_UNREACH_HOST_PROHIB:
111        case ICMP_UNREACH_FILTER_PROHIB:
112        case ICMP_UNREACH_PRECEDENCE_CUTOFF:
113          return ICMP6_DST_UNREACH_ADMIN;
114
115        // Otherwise, we don't understand this ICMP type/code combination. Fall through.
116      }
117  }
118  logmsg_dbg(ANDROID_LOG_DEBUG, "icmp_to_icmp6_code: unhandled ICMP type/code %d/%d", type, code);
119  return 0;
120}
121
122/* function: icmp6_to_icmp_type
123 * Maps ICMPv6 types to ICMP types. Partial implementation of RFC 6145, section 5.2.
124 * type - the ICMP type
125 */
126uint8_t icmp6_to_icmp_type(uint8_t type, uint8_t code) {
127  switch (type) {
128    case ICMP6_ECHO_REQUEST:
129      return ICMP_ECHO;
130
131    case ICMP6_ECHO_REPLY:
132      return ICMP_ECHOREPLY;
133
134    case ICMP6_DST_UNREACH:
135      return ICMP_DEST_UNREACH;
136
137    case ICMP6_TIME_EXCEEDED:
138      return ICMP_TIME_EXCEEDED;
139  }
140
141  // We don't understand this ICMP type. Return parameter problem so the caller will bail out.
142  logmsg_dbg(ANDROID_LOG_DEBUG, "icmp6_to_icmp_type: unhandled ICMP type/code %d/%d", type, code);
143  return ICMP_PARAMETERPROB;
144}
145
146/* function: icmp6_to_icmp_code
147 * Maps ICMPv6 codes to ICMP codes. Partial implementation of RFC 6145, section 5.2.
148 * type - the ICMPv6 type
149 * code - the ICMPv6 code
150 */
151uint8_t icmp6_to_icmp_code(uint8_t type, uint8_t code) {
152  switch (type) {
153    case ICMP6_ECHO_REQUEST:
154    case ICMP6_ECHO_REPLY:
155    case ICMP6_TIME_EXCEEDED:
156      return code;
157
158    case ICMP6_DST_UNREACH:
159      switch (code) {
160        case ICMP6_DST_UNREACH_NOROUTE:
161          return ICMP_UNREACH_HOST;
162
163        case ICMP6_DST_UNREACH_ADMIN:
164          return ICMP_UNREACH_HOST_PROHIB;
165
166        case ICMP6_DST_UNREACH_BEYONDSCOPE:
167          return ICMP_UNREACH_HOST;
168
169        case ICMP6_DST_UNREACH_ADDR:
170          return ICMP_HOST_UNREACH;
171
172        case ICMP6_DST_UNREACH_NOPORT:
173          return ICMP_UNREACH_PORT;
174
175        // Otherwise, we don't understand this ICMPv6 type/code combination. Fall through.
176      }
177  }
178
179  logmsg_dbg(ANDROID_LOG_DEBUG, "icmp6_to_icmp_code: unhandled ICMP type/code %d/%d", type, code);
180  return 0;
181}
182