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 <linux/icmp.h>
20 #include <netinet/icmp6.h>
21 #include <netinet/in.h>
22 #include <netinet/ip_icmp.h>
23 
24 #include "icmp.h"
25 #include "logging.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) { return type == 3 || type == 11 || type == 12; }
48 
49 /* function: is_icmp6_error
50  * Determines whether an ICMPv6 type is an error message.
51  * type: the ICMPv6 type
52  */
is_icmp6_error(uint8_t type)53 int is_icmp6_error(uint8_t type) { return type < 128; }
54 
55 /* function: icmp_to_icmp6_type
56  * Maps ICMP types to ICMPv6 types. Partial implementation of RFC 6145, section 4.2.
57  * type - the ICMPv6 type
58  */
icmp_to_icmp6_type(uint8_t type,uint8_t code)59 uint8_t icmp_to_icmp6_type(uint8_t type, uint8_t code) {
60   switch (type) {
61     case ICMP_ECHO:
62       return ICMP6_ECHO_REQUEST;
63 
64     case ICMP_ECHOREPLY:
65       return ICMP6_ECHO_REPLY;
66 
67     case ICMP_TIME_EXCEEDED:
68       return ICMP6_TIME_EXCEEDED;
69 
70     case ICMP_DEST_UNREACH:
71       // These two types need special translation which we don't support yet.
72       if (code != ICMP_UNREACH_PROTOCOL && code != ICMP_UNREACH_NEEDFRAG) {
73         return ICMP6_DST_UNREACH;
74       }
75   }
76 
77   // We don't understand this ICMP type. Return parameter problem so the caller will bail out.
78   logmsg_dbg(ANDROID_LOG_DEBUG, "icmp_to_icmp6_type: unhandled ICMP type %d", type);
79   return ICMP6_PARAM_PROB;
80 }
81 
82 /* function: icmp_to_icmp6_code
83  * Maps ICMP codes to ICMPv6 codes. Partial implementation of RFC 6145, section 4.2.
84  * type - the ICMP type
85  * code - the ICMP code
86  */
icmp_to_icmp6_code(uint8_t type,uint8_t code)87 uint8_t icmp_to_icmp6_code(uint8_t type, uint8_t code) {
88   switch (type) {
89     case ICMP_ECHO:
90     case ICMP_ECHOREPLY:
91       return 0;
92 
93     case ICMP_TIME_EXCEEDED:
94       return code;
95 
96     case ICMP_DEST_UNREACH:
97       switch (code) {
98         case ICMP_UNREACH_NET:
99         case ICMP_UNREACH_HOST:
100           return ICMP6_DST_UNREACH_NOROUTE;
101 
102         case ICMP_UNREACH_PORT:
103           return ICMP6_DST_UNREACH_NOPORT;
104 
105         case ICMP_UNREACH_NET_PROHIB:
106         case ICMP_UNREACH_HOST_PROHIB:
107         case ICMP_UNREACH_FILTER_PROHIB:
108         case ICMP_UNREACH_PRECEDENCE_CUTOFF:
109           return ICMP6_DST_UNREACH_ADMIN;
110 
111           // Otherwise, we don't understand this ICMP type/code combination. Fall through.
112       }
113   }
114   logmsg_dbg(ANDROID_LOG_DEBUG, "icmp_to_icmp6_code: unhandled ICMP type/code %d/%d", type, code);
115   return 0;
116 }
117 
118 /* function: icmp6_to_icmp_type
119  * Maps ICMPv6 types to ICMP types. Partial implementation of RFC 6145, section 5.2.
120  * type - the ICMP type
121  */
icmp6_to_icmp_type(uint8_t type,uint8_t code)122 uint8_t icmp6_to_icmp_type(uint8_t type, uint8_t code) {
123   switch (type) {
124     case ICMP6_ECHO_REQUEST:
125       return ICMP_ECHO;
126 
127     case ICMP6_ECHO_REPLY:
128       return ICMP_ECHOREPLY;
129 
130     case ICMP6_DST_UNREACH:
131       return ICMP_DEST_UNREACH;
132 
133     case ICMP6_TIME_EXCEEDED:
134       return ICMP_TIME_EXCEEDED;
135   }
136 
137   // We don't understand this ICMP type. Return parameter problem so the caller will bail out.
138   logmsg_dbg(ANDROID_LOG_DEBUG, "icmp6_to_icmp_type: unhandled ICMP type/code %d/%d", type, code);
139   return ICMP_PARAMETERPROB;
140 }
141 
142 /* function: icmp6_to_icmp_code
143  * Maps ICMPv6 codes to ICMP codes. Partial implementation of RFC 6145, section 5.2.
144  * type - the ICMPv6 type
145  * code - the ICMPv6 code
146  */
icmp6_to_icmp_code(uint8_t type,uint8_t code)147 uint8_t icmp6_to_icmp_code(uint8_t type, uint8_t code) {
148   switch (type) {
149     case ICMP6_ECHO_REQUEST:
150     case ICMP6_ECHO_REPLY:
151     case ICMP6_TIME_EXCEEDED:
152       return code;
153 
154     case ICMP6_DST_UNREACH:
155       switch (code) {
156         case ICMP6_DST_UNREACH_NOROUTE:
157           return ICMP_UNREACH_HOST;
158 
159         case ICMP6_DST_UNREACH_ADMIN:
160           return ICMP_UNREACH_HOST_PROHIB;
161 
162         case ICMP6_DST_UNREACH_BEYONDSCOPE:
163           return ICMP_UNREACH_HOST;
164 
165         case ICMP6_DST_UNREACH_ADDR:
166           return ICMP_HOST_UNREACH;
167 
168         case ICMP6_DST_UNREACH_NOPORT:
169           return ICMP_UNREACH_PORT;
170 
171           // Otherwise, we don't understand this ICMPv6 type/code combination. Fall through.
172       }
173   }
174 
175   logmsg_dbg(ANDROID_LOG_DEBUG, "icmp6_to_icmp_code: unhandled ICMP type/code %d/%d", type, code);
176   return 0;
177 }
178