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  */
icmp_guess_ttl(uint8_t ttl)31 uint8_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  */
is_icmp_error(uint8_t type)47 int 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  */
is_icmp6_error(uint8_t type)55 int 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  */
icmp_to_icmp6_type(uint8_t type,uint8_t code)63 uint8_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  */
icmp_to_icmp6_code(uint8_t type,uint8_t code)91 uint8_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  */
icmp6_to_icmp_type(uint8_t type,uint8_t code)126 uint8_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  */
icmp6_to_icmp_code(uint8_t type,uint8_t code)151 uint8_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