1 /* $NetBSD: icmp.c,v 1.3 2022/04/03 01:10:58 christos Exp $ */ 2 3 /* dhcp.c 4 5 ICMP Protocol engine - for sending out pings and receiving 6 responses. */ 7 8 /* 9 * Copyright (C) 2004-2022 Internet Systems Consortium, Inc. ("ISC") 10 * Copyright (c) 1996-2003 by Internet Software Consortium 11 * 12 * This Source Code Form is subject to the terms of the Mozilla Public 13 * License, v. 2.0. If a copy of the MPL was not distributed with this 14 * file, You can obtain one at http://mozilla.org/MPL/2.0/. 15 * 16 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES 17 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 18 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR 19 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 20 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 21 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 22 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 23 * 24 * Internet Systems Consortium, Inc. 25 * PO Box 360 26 * Newmarket, NH 03857 USA 27 * <info@isc.org> 28 * https://www.isc.org/ 29 * 30 */ 31 32 #include <sys/cdefs.h> 33 __RCSID("$NetBSD: icmp.c,v 1.3 2022/04/03 01:10:58 christos Exp $"); 34 35 #include "dhcpd.h" 36 #include "netinet/ip.h" 37 #include "netinet/ip_icmp.h" 38 39 struct icmp_state *icmp_state; 40 static omapi_object_type_t *dhcp_type_icmp; 41 static int no_icmp; 42 43 OMAPI_OBJECT_ALLOC (icmp_state, struct icmp_state, dhcp_type_icmp) 44 45 #if defined (TRACING) 46 trace_type_t *trace_icmp_input; 47 trace_type_t *trace_icmp_output; 48 #endif 49 50 /* Initialize the ICMP protocol. */ 51 52 void icmp_startup (routep, handler) 53 int routep; 54 void (*handler) (struct iaddr, u_int8_t *, int); 55 { 56 struct protoent *proto; 57 int protocol = 1; 58 int state; 59 isc_result_t result; 60 61 /* Only initialize icmp once. */ 62 if (dhcp_type_icmp) 63 log_fatal ("attempted to reinitialize icmp protocol"); 64 65 result = omapi_object_type_register (&dhcp_type_icmp, "icmp", 66 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 67 sizeof (struct icmp_state), 68 0, RC_MISC); 69 70 if (result != ISC_R_SUCCESS) 71 log_fatal ("Can't register icmp object type: %s", 72 isc_result_totext (result)); 73 74 icmp_state_allocate (&icmp_state, MDL); 75 icmp_state -> icmp_handler = handler; 76 77 #if defined (TRACING) 78 trace_icmp_input = trace_type_register ("icmp-input", (void *)0, 79 trace_icmp_input_input, 80 trace_icmp_input_stop, MDL); 81 trace_icmp_output = trace_type_register ("icmp-output", (void *)0, 82 trace_icmp_output_input, 83 trace_icmp_output_stop, MDL); 84 85 /* If we're playing back a trace file, don't create the socket 86 or set up the callback. */ 87 if (!trace_playback ()) { 88 #endif 89 /* Get the protocol number (should be 1). */ 90 proto = getprotobyname ("icmp"); 91 if (proto) 92 protocol = proto -> p_proto; 93 94 /* Get a raw socket for the ICMP protocol. */ 95 icmp_state -> socket = socket (AF_INET, SOCK_RAW, protocol); 96 if (icmp_state -> socket < 0) { 97 no_icmp = 1; 98 log_error ("unable to create icmp socket: %m"); 99 return; 100 } 101 102 #if defined (HAVE_SETFD) 103 if (fcntl (icmp_state -> socket, F_SETFD, 1) < 0) 104 log_error ("Can't set close-on-exec on icmp: %m"); 105 #endif 106 107 /* Make sure it does routing... */ 108 state = 0; 109 if (setsockopt (icmp_state -> socket, SOL_SOCKET, SO_DONTROUTE, 110 (char *)&state, sizeof state) < 0) 111 log_fatal ("Can't disable SO_DONTROUTE on ICMP: %m"); 112 113 result = (omapi_register_io_object 114 ((omapi_object_t *)icmp_state, 115 icmp_readsocket, 0, icmp_echoreply, 0, 0)); 116 if (result != ISC_R_SUCCESS) 117 log_fatal ("Can't register icmp handle: %s", 118 isc_result_totext (result)); 119 #if defined (TRACING) 120 } 121 #endif 122 } 123 124 int icmp_readsocket (h) 125 omapi_object_t *h; 126 { 127 struct icmp_state *state; 128 129 state = (struct icmp_state *)h; 130 return state -> socket; 131 } 132 133 int icmp_echorequest (addr) 134 struct iaddr *addr; 135 { 136 struct sockaddr_in to; 137 struct icmp icmp; 138 int status; 139 #if defined (TRACING) 140 trace_iov_t iov [2]; 141 #endif 142 143 if (no_icmp) 144 return 1; 145 if (!icmp_state) 146 log_fatal ("ICMP protocol used before initialization."); 147 148 memset (&to, 0, sizeof(to)); 149 #ifdef HAVE_SA_LEN 150 to.sin_len = sizeof to; 151 #endif 152 to.sin_family = AF_INET; 153 to.sin_port = 0; /* unused. */ 154 memcpy (&to.sin_addr, addr -> iabuf, sizeof to.sin_addr); /* XXX */ 155 156 icmp.icmp_type = ICMP_ECHO; 157 icmp.icmp_code = 0; 158 icmp.icmp_cksum = 0; 159 icmp.icmp_seq = 0; 160 #ifdef _LP64 161 icmp.icmp_id = (((u_int32_t)(u_int64_t)addr) ^ 162 (u_int32_t)(((u_int64_t)addr) >> 32)); 163 #else 164 icmp.icmp_id = (u_int32_t)addr; 165 #endif 166 memset (&icmp.icmp_dun, 0, sizeof icmp.icmp_dun); 167 168 icmp.icmp_cksum = wrapsum (checksum ((unsigned char *)&icmp, 169 sizeof icmp, 0)); 170 171 #if defined (TRACING) 172 if (trace_playback ()) { 173 char *buf = (char *)0; 174 unsigned buflen = 0; 175 176 /* Consume the ICMP event. */ 177 status = trace_get_packet (&trace_icmp_output, &buflen, &buf); 178 if (status != ISC_R_SUCCESS) 179 log_error ("icmp_echorequest: %s", 180 isc_result_totext (status)); 181 if (buf) 182 dfree (buf, MDL); 183 } else { 184 if (trace_record ()) { 185 iov [0].buf = (char *)addr; 186 iov [0].len = sizeof *addr; 187 iov [1].buf = (char *)&icmp; 188 iov [1].len = sizeof icmp; 189 trace_write_packet_iov (trace_icmp_output, 190 2, iov, MDL); 191 } 192 #endif 193 /* Send the ICMP packet... */ 194 status = sendto (icmp_state -> socket, 195 (char *)&icmp, sizeof icmp, 0, 196 (struct sockaddr *)&to, sizeof to); 197 if (status < 0) 198 log_error ("icmp_echorequest %s: %m", 199 inet_ntoa(to.sin_addr)); 200 201 if (status != sizeof icmp) 202 return 0; 203 #if defined (TRACING) 204 } 205 #endif 206 return 1; 207 } 208 209 isc_result_t icmp_echoreply (h) 210 omapi_object_t *h; 211 { 212 struct icmp *icfrom; 213 struct ip *ip; 214 struct sockaddr_in from; 215 u_int8_t icbuf [1500]; 216 int status; 217 SOCKLEN_T sl; 218 int hlen, len; 219 struct iaddr ia; 220 struct icmp_state *state; 221 #if defined (TRACING) 222 trace_iov_t iov [2]; 223 #endif 224 225 state = (struct icmp_state *)h; 226 227 sl = sizeof from; 228 status = recvfrom (state -> socket, (char *)icbuf, sizeof icbuf, 0, 229 (struct sockaddr *)&from, &sl); 230 if (status < 0) { 231 log_error ("icmp_echoreply: %m"); 232 return ISC_R_UNEXPECTED; 233 } 234 235 /* Find the IP header length... */ 236 ip = (struct ip *)icbuf; 237 hlen = IP_HL (ip); 238 239 /* Short packet? */ 240 if (status < hlen + (sizeof *icfrom)) { 241 return ISC_R_SUCCESS; 242 } 243 244 len = status - hlen; 245 icfrom = (struct icmp *)(icbuf + hlen); 246 247 /* Silently discard ICMP packets that aren't echoreplies. */ 248 if (icfrom -> icmp_type != ICMP_ECHOREPLY) { 249 return ISC_R_SUCCESS; 250 } 251 252 /* If we were given a second-stage handler, call it. */ 253 if (state -> icmp_handler) { 254 memcpy (ia.iabuf, &from.sin_addr, sizeof from.sin_addr); 255 ia.len = sizeof from.sin_addr; 256 257 #if defined (TRACING) 258 if (trace_record ()) { 259 ia.len = htonl(ia.len); 260 iov [0].buf = (char *)&ia; 261 iov [0].len = sizeof ia; 262 iov [1].buf = (char *)icbuf; 263 iov [1].len = len; 264 trace_write_packet_iov (trace_icmp_input, 2, iov, MDL); 265 ia.len = ntohl(ia.len); 266 } 267 #endif 268 (*state -> icmp_handler) (ia, icbuf, len); 269 } 270 return ISC_R_SUCCESS; 271 } 272 273 #if defined (TRACING) 274 void trace_icmp_input_input (trace_type_t *ttype, unsigned length, char *buf) 275 { 276 struct iaddr *ia; 277 u_int8_t *icbuf; 278 ia = (struct iaddr *)buf; 279 ia->len = ntohl(ia->len); 280 icbuf = (u_int8_t *)(ia + 1); 281 if (icmp_state -> icmp_handler) 282 (*icmp_state -> icmp_handler) (*ia, icbuf, 283 (int)(length - sizeof ia)); 284 } 285 286 void trace_icmp_input_stop (trace_type_t *ttype) { } 287 288 void trace_icmp_output_input (trace_type_t *ttype, unsigned length, char *buf) 289 { 290 struct iaddr ia; 291 292 if (length != (sizeof (struct icmp) + sizeof (ia))) { 293 log_error ("trace_icmp_output_input: data size mismatch %d:%d", 294 length, (int)(sizeof (struct icmp) + sizeof (ia))); 295 return; 296 } 297 ia.len = 4; 298 memcpy (ia.iabuf, buf, 4); 299 300 log_error ("trace_icmp_output_input: unsent ping to %s", piaddr (ia)); 301 } 302 303 void trace_icmp_output_stop (trace_type_t *ttype) { } 304 #endif /* TRACING */ 305