summaryrefslogtreecommitdiffstats
path: root/icmp.c
blob: 75a4a4d1c4d709a0a0ed17b78a27d389905fdb53 (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
/*
 * Copyright (C) 2013 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * icmp.c - convenience functions for translating ICMP and ICMPv6 packets.
 */

#include <netinet/in.h>
#include <netinet/ip_icmp.h>
#include <netinet/icmp6.h>
#include <linux/icmp.h>

#include "logging.h"
#include "icmp.h"

/* function: icmp_guess_ttl
 * Guesses the number of hops a received packet has traversed based on its TTL.
 * ttl - the ttl of the received packet.
 */
uint8_t icmp_guess_ttl(uint8_t ttl) {
  if (ttl > 128) {
    return 255 - ttl;
  } else if (ttl > 64) {
    return 128 - ttl;
  } else if (ttl > 32) {
    return 64 - ttl;
  } else {
    return 32 - ttl;
  }
}

/* function: is_icmp_error
 * Determines whether an ICMP type is an error message.
 * type: the ICMP type
 */
int is_icmp_error(uint8_t type) {
  return type == 3 || type == 11 || type == 12;
}

/* function: is_icmp6_error
 * Determines whether an ICMPv6 type is an error message.
 * type: the ICMPv6 type
 */
int is_icmp6_error(uint8_t type) {
  return type < 128;
}

/* function: icmp_to_icmp6_type
 * Maps ICMP types to ICMPv6 types. Partial implementation of RFC 6145, section 4.2.
 * type - the ICMPv6 type
 */
uint8_t icmp_to_icmp6_type(uint8_t type, uint8_t code) {
  switch (type) {
    case ICMP_ECHO:
      return ICMP6_ECHO_REQUEST;

    case ICMP_ECHOREPLY:
      return ICMP6_ECHO_REPLY;

    case ICMP_TIME_EXCEEDED:
      return ICMP6_TIME_EXCEEDED;

    case ICMP_DEST_UNREACH:
      // These two types need special translation which we don't support yet.
      if (code != ICMP_UNREACH_PROTOCOL && code != ICMP_UNREACH_NEEDFRAG) {
        return ICMP6_DST_UNREACH;
      }
  }

  // We don't understand this ICMP type. Return parameter problem so the caller will bail out.
  logmsg_dbg(ANDROID_LOG_DEBUG, "icmp_to_icmp6_type: unhandled ICMP type %d", type);
  return ICMP6_PARAM_PROB;
}

/* function: icmp_to_icmp6_code
 * Maps ICMP codes to ICMPv6 codes. Partial implementation of RFC 6145, section 4.2.
 * type - the ICMP type
 * code - the ICMP code
 */
uint8_t icmp_to_icmp6_code(uint8_t type, uint8_t code) {
  switch (type) {
    case ICMP_ECHO:
    case ICMP_ECHOREPLY:
      return 0;

    case ICMP_TIME_EXCEEDED:
      return code;

    case ICMP_DEST_UNREACH:
      switch (code) {
        case ICMP_UNREACH_NET:
        case ICMP_UNREACH_HOST:
          return ICMP6_DST_UNREACH_NOROUTE;

        case ICMP_UNREACH_PORT:
          return ICMP6_DST_UNREACH_NOPORT;

        case ICMP_UNREACH_NET_PROHIB:
        case ICMP_UNREACH_HOST_PROHIB:
        case ICMP_UNREACH_FILTER_PROHIB:
        case ICMP_UNREACH_PRECEDENCE_CUTOFF:
          return ICMP6_DST_UNREACH_ADMIN;

        // Otherwise, we don't understand this ICMP type/code combination. Fall through.
      }
  }
  logmsg_dbg(ANDROID_LOG_DEBUG, "icmp_to_icmp6_code: unhandled ICMP type/code %d/%d", type, code);
  return 0;
}

/* function: icmp6_to_icmp_type
 * Maps ICMPv6 types to ICMP types. Partial implementation of RFC 6145, section 5.2.
 * type - the ICMP type
 */
uint8_t icmp6_to_icmp_type(uint8_t type, uint8_t code) {
  switch (type) {
    case ICMP6_ECHO_REQUEST:
      return ICMP_ECHO;

    case ICMP6_ECHO_REPLY:
      return ICMP_ECHOREPLY;

    case ICMP6_DST_UNREACH:
      return ICMP_DEST_UNREACH;

    case ICMP6_TIME_EXCEEDED:
      return ICMP_TIME_EXCEEDED;
  }

  // We don't understand this ICMP type. Return parameter problem so the caller will bail out.
  logmsg_dbg(ANDROID_LOG_DEBUG, "icmp6_to_icmp_type: unhandled ICMP type/code %d/%d", type, code);
  return ICMP_PARAMETERPROB;
}

/* function: icmp6_to_icmp_code
 * Maps ICMPv6 codes to ICMP codes. Partial implementation of RFC 6145, section 5.2.
 * type - the ICMPv6 type
 * code - the ICMPv6 code
 */
uint8_t icmp6_to_icmp_code(uint8_t type, uint8_t code) {
  switch (type) {
    case ICMP6_ECHO_REQUEST:
    case ICMP6_ECHO_REPLY:
    case ICMP6_TIME_EXCEEDED:
      return code;

    case ICMP6_DST_UNREACH:
      switch (code) {
        case ICMP6_DST_UNREACH_NOROUTE:
          return ICMP_UNREACH_HOST;

        case ICMP6_DST_UNREACH_ADMIN:
          return ICMP_UNREACH_HOST_PROHIB;

        case ICMP6_DST_UNREACH_BEYONDSCOPE:
          return ICMP_UNREACH_HOST;

        case ICMP6_DST_UNREACH_ADDR:
          return ICMP_HOST_UNREACH;

        case ICMP6_DST_UNREACH_NOPORT:
          return ICMP_UNREACH_PORT;

        // Otherwise, we don't understand this ICMPv6 type/code combination. Fall through.
      }
  }

  logmsg_dbg(ANDROID_LOG_DEBUG, "icmp6_to_icmp_code: unhandled ICMP type/code %d/%d", type, code);
  return 0;
}