1 /* 2 * Copyright (c) 1982, 1986, 1988, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. Neither the name of the University nor the names of its contributors 14 * may be used to endorse or promote products derived from this software 15 * without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 * 29 * @(#)ip_icmp.c 8.2 (Berkeley) 1/4/94 30 * ip_icmp.c,v 1.7 1995/05/30 08:09:42 rgrimes Exp 31 */ 32 33 #include "slirp.h" 34 #include "ip_icmp.h" 35 #include "android/sockets.h" 36 37 #ifdef LOG_ENABLED 38 struct icmpstat icmpstat; 39 #endif 40 41 /* The message sent when emulating PING */ 42 /* Be nice and tell them it's just a pseudo-ping packet */ 43 static const char icmp_ping_msg[] = "This is a pseudo-PING packet used by Slirp to emulate ICMP ECHO-REQUEST packets.\n"; 44 45 /* list of actions for icmp_error() on RX of an icmp message */ 46 static const int icmp_flush[19] = { 47 /* ECHO REPLY (0) */ 0, 48 1, 49 1, 50 /* DEST UNREACH (3) */ 1, 51 /* SOURCE QUENCH (4)*/ 1, 52 /* REDIRECT (5) */ 1, 53 1, 54 1, 55 /* ECHO (8) */ 0, 56 /* ROUTERADVERT (9) */ 1, 57 /* ROUTERSOLICIT (10) */ 1, 58 /* TIME EXCEEDED (11) */ 1, 59 /* PARAMETER PROBLEM (12) */ 1, 60 /* TIMESTAMP (13) */ 0, 61 /* TIMESTAMP REPLY (14) */ 0, 62 /* INFO (15) */ 0, 63 /* INFO REPLY (16) */ 0, 64 /* ADDR MASK (17) */ 0, 65 /* ADDR MASK REPLY (18) */ 0 66 }; 67 68 /* 69 * Process a received ICMP message. 70 */ 71 void 72 icmp_input(struct mbuf *m, int hlen) 73 { 74 register struct icmp *icp; 75 register struct ip *ip=mtod(m, struct ip *); 76 int icmplen=ip->ip_len; 77 /* int code; */ 78 79 DEBUG_CALL("icmp_input"); 80 DEBUG_ARG("m = %lx", (long )m); 81 DEBUG_ARG("m_len = %d", m->m_len); 82 83 STAT(icmpstat.icps_received++); 84 85 /* 86 * Locate icmp structure in mbuf, and check 87 * that its not corrupted and of at least minimum length. 88 */ 89 if (icmplen < ICMP_MINLEN) { /* min 8 bytes payload */ 90 STAT(icmpstat.icps_tooshort++); 91 freeit: 92 m_freem(m); 93 goto end_error; 94 } 95 96 m->m_len -= hlen; 97 m->m_data += hlen; 98 icp = mtod(m, struct icmp *); 99 if (cksum(m, icmplen)) { 100 STAT(icmpstat.icps_checksum++); 101 goto freeit; 102 } 103 m->m_len += hlen; 104 m->m_data -= hlen; 105 106 /* icmpstat.icps_inhist[icp->icmp_type]++; */ 107 /* code = icp->icmp_code; */ 108 109 DEBUG_ARG("icmp_type = %d", icp->icmp_type); 110 switch (icp->icmp_type) { 111 case ICMP_ECHO: 112 icp->icmp_type = ICMP_ECHOREPLY; 113 ip->ip_len += hlen; /* since ip_input subtracts this */ 114 if (ip_geth(ip->ip_dst) == alias_addr_ip) { 115 icmp_reflect(m); 116 } else { 117 struct socket *so; 118 SockAddress addr; 119 uint32_t addr_ip; 120 uint16_t addr_port; 121 122 if ((so = socreate()) == NULL) goto freeit; 123 if(udp_attach(so) == -1) { 124 DEBUG_MISC((dfd,"icmp_input udp_attach errno = %d-%s\n", 125 errno,errno_str)); 126 sofree(so); 127 m_free(m); 128 goto end_error; 129 } 130 so->so_m = m; 131 so->so_faddr_ip = ip_geth(ip->ip_dst); 132 so->so_faddr_port = 7; 133 so->so_laddr_ip = ip_geth(ip->ip_src); 134 so->so_laddr_port = 9; 135 so->so_iptos = ip->ip_tos; 136 so->so_type = IPPROTO_ICMP; 137 so->so_state = SS_ISFCONNECTED; 138 139 /* Send the packet */ 140 if ((so->so_faddr_ip & 0xffffff00) == special_addr_ip) { 141 /* It's an alias */ 142 int low = so->so_faddr_ip & 0xff; 143 144 if (low >= CTL_DNS && low < CTL_DNS + dns_addr_count) 145 addr_ip = dns_addr[low - CTL_DNS]; 146 else 147 addr_ip = loopback_addr_ip; 148 } else { 149 addr_ip = so->so_faddr_ip; 150 } 151 addr_port = so->so_faddr_port; 152 153 sock_address_init_inet( &addr, addr_ip, addr_port ); 154 155 if(socket_sendto(so->s, icmp_ping_msg, strlen(icmp_ping_msg), &addr) < 0) { 156 DEBUG_MISC((dfd,"icmp_input udp sendto tx errno = %d-%s\n", 157 errno,errno_str)); 158 icmp_error(m, ICMP_UNREACH,ICMP_UNREACH_NET, 0,errno_str); 159 udp_detach(so); 160 } 161 } /* if ip->ip_dst.s_addr == alias_addr.s_addr */ 162 break; 163 case ICMP_UNREACH: 164 /* XXX? report error? close socket? */ 165 case ICMP_TIMXCEED: 166 case ICMP_PARAMPROB: 167 case ICMP_SOURCEQUENCH: 168 case ICMP_TSTAMP: 169 case ICMP_MASKREQ: 170 case ICMP_REDIRECT: 171 STAT(icmpstat.icps_notsupp++); 172 m_freem(m); 173 break; 174 175 default: 176 STAT(icmpstat.icps_badtype++); 177 m_freem(m); 178 } /* swith */ 179 180 end_error: 181 /* m is m_free()'d xor put in a socket xor or given to ip_send */ 182 return; 183 } 184 185 186 /* 187 * Send an ICMP message in response to a situation 188 * 189 * RFC 1122: 3.2.2 MUST send at least the IP header and 8 bytes of header. MAY send more (we do). 190 * MUST NOT change this header information. 191 * MUST NOT reply to a multicast/broadcast IP address. 192 * MUST NOT reply to a multicast/broadcast MAC address. 193 * MUST reply to only the first fragment. 194 */ 195 /* 196 * Send ICMP_UNREACH back to the source regarding msrc. 197 * mbuf *msrc is used as a template, but is NOT m_free()'d. 198 * It is reported as the bad ip packet. The header should 199 * be fully correct and in host byte order. 200 * ICMP fragmentation is illegal. All machines must accept 576 bytes in one 201 * packet. The maximum payload is 576-20(ip hdr)-8(icmp hdr)=548 202 */ 203 204 #define ICMP_MAXDATALEN (IP_MSS-28) 205 void 206 icmp_error(struct mbuf *msrc, u_char type, u_char code, int minsize, 207 const char *message) 208 { 209 unsigned hlen, shlen, s_ip_len; 210 register struct ip *ip; 211 register struct icmp *icp; 212 register struct mbuf *m; 213 214 DEBUG_CALL("icmp_error"); 215 DEBUG_ARG("msrc = %lx", (long )msrc); 216 DEBUG_ARG("msrc_len = %d", msrc->m_len); 217 218 if(type!=ICMP_UNREACH && type!=ICMP_TIMXCEED) goto end_error; 219 220 /* check msrc */ 221 if(!msrc) goto end_error; 222 ip = mtod(msrc, struct ip *); 223 #ifdef DEBUG 224 { char bufa[20], bufb[20]; 225 strcpy(bufa, inet_iptostr(ip_geth(ip->ip_src))); 226 strcpy(bufb, inet_iptostr(ip_geth(ip->ip_dst))); 227 DEBUG_MISC((dfd, " %.16s to %.16s\n", bufa, bufb)); 228 } 229 #endif 230 if(ip->ip_off & IP_OFFMASK) goto end_error; /* Only reply to fragment 0 */ 231 232 shlen=ip->ip_hl << 2; 233 s_ip_len=ip->ip_len; 234 if(ip->ip_p == IPPROTO_ICMP) { 235 icp = (struct icmp *)((char *)ip + shlen); 236 /* 237 * Assume any unknown ICMP type is an error. This isn't 238 * specified by the RFC, but think about it.. 239 */ 240 if(icp->icmp_type>18 || icmp_flush[icp->icmp_type]) goto end_error; 241 } 242 243 /* make a copy */ 244 if(!(m=m_get())) goto end_error; /* get mbuf */ 245 { int new_m_size; 246 new_m_size=sizeof(struct ip )+ICMP_MINLEN+msrc->m_len+ICMP_MAXDATALEN; 247 if(new_m_size>m->m_size) m_inc(m, new_m_size); 248 } 249 memcpy(m->m_data, msrc->m_data, msrc->m_len); 250 m->m_len = msrc->m_len; /* copy msrc to m */ 251 252 /* make the header of the reply packet */ 253 ip = mtod(m, struct ip *); 254 hlen= sizeof(struct ip ); /* no options in reply */ 255 256 /* fill in icmp */ 257 m->m_data += hlen; 258 m->m_len -= hlen; 259 260 icp = mtod(m, struct icmp *); 261 262 if(minsize) s_ip_len=shlen+ICMP_MINLEN; /* return header+8b only */ 263 else if(s_ip_len>ICMP_MAXDATALEN) /* maximum size */ 264 s_ip_len=ICMP_MAXDATALEN; 265 266 m->m_len=ICMP_MINLEN+s_ip_len; /* 8 bytes ICMP header */ 267 268 /* min. size = 8+sizeof(struct ip)+8 */ 269 270 icp->icmp_type = type; 271 icp->icmp_code = code; 272 icp->icmp_id = 0; 273 icp->icmp_seq = 0; 274 275 memcpy(&icp->icmp_ip, msrc->m_data, s_ip_len); /* report the ip packet */ 276 HTONS(icp->icmp_ip.ip_len); 277 HTONS(icp->icmp_ip.ip_id); 278 HTONS(icp->icmp_ip.ip_off); 279 280 #ifdef DEBUG 281 if(message) { /* DEBUG : append message to ICMP packet */ 282 int message_len; 283 char *cpnt; 284 message_len=strlen(message); 285 if(message_len>ICMP_MAXDATALEN) message_len=ICMP_MAXDATALEN; 286 cpnt=(char *)m->m_data+m->m_len; 287 memcpy(cpnt, message, message_len); 288 m->m_len+=message_len; 289 } 290 #endif 291 292 icp->icmp_cksum = 0; 293 icp->icmp_cksum = cksum(m, m->m_len); 294 295 m->m_data -= hlen; 296 m->m_len += hlen; 297 298 /* fill in ip */ 299 ip->ip_hl = hlen >> 2; 300 ip->ip_len = m->m_len; 301 302 ip->ip_tos=((ip->ip_tos & 0x1E) | 0xC0); /* high priority for errors */ 303 304 ip->ip_ttl = MAXTTL; 305 ip->ip_p = IPPROTO_ICMP; 306 ip->ip_dst = ip->ip_src; /* ip adresses */ 307 ip->ip_src = ip_setn(alias_addr_ip); 308 309 (void ) ip_output((struct socket *)NULL, m); 310 311 STAT(icmpstat.icps_reflect++); 312 313 end_error: 314 return; 315 } 316 #undef ICMP_MAXDATALEN 317 318 /* 319 * Reflect the ip packet back to the source 320 */ 321 void 322 icmp_reflect(struct mbuf *m) 323 { 324 register struct ip *ip = mtod(m, struct ip *); 325 int hlen = ip->ip_hl << 2; 326 int optlen = hlen - sizeof(struct ip ); 327 register struct icmp *icp; 328 329 /* 330 * Send an icmp packet back to the ip level, 331 * after supplying a checksum. 332 */ 333 m->m_data += hlen; 334 m->m_len -= hlen; 335 icp = mtod(m, struct icmp *); 336 337 icp->icmp_cksum = 0; 338 icp->icmp_cksum = cksum(m, ip->ip_len - hlen); 339 340 m->m_data -= hlen; 341 m->m_len += hlen; 342 343 /* fill in ip */ 344 if (optlen > 0) { 345 /* 346 * Strip out original options by copying rest of first 347 * mbuf's data back, and adjust the IP length. 348 */ 349 memmove((caddr_t)(ip + 1), (caddr_t)ip + hlen, 350 (unsigned )(m->m_len - hlen)); 351 hlen -= optlen; 352 ip->ip_hl = hlen >> 2; 353 ip->ip_len -= optlen; 354 m->m_len -= optlen; 355 } 356 357 ip->ip_ttl = MAXTTL; 358 { /* swap */ 359 ipaddr_t icmp_dst; 360 icmp_dst = ip->ip_dst; 361 ip->ip_dst = ip->ip_src; 362 ip->ip_src = icmp_dst; 363 } 364 365 (void ) ip_output((struct socket *)NULL, m); 366 367 STAT(icmpstat.icps_reflect++); 368 } 369