1 /* $NetBSD: icmp.c,v 1.3 2014/07/12 12:09:37 spz Exp $ */
4 ICMP Protocol engine - for sending out pings and receiving
8 * Copyright (c) 2011,2013,2014 by Internet Systems Consortium, Inc. ("ISC")
9 * Copyright (c) 2004,2007,2009 by Internet Systems Consortium, Inc. ("ISC")
10 * Copyright (c) 1996-2003 by Internet Software Consortium
12 * Permission to use, copy, modify, and distribute this software for any
13 * purpose with or without fee is hereby granted, provided that the above
14 * copyright notice and this permission notice appear in all copies.
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.
24 * Internet Systems Consortium, Inc.
26 * Redwood City, CA 94063
28 * https://www.isc.org/
32 #include <sys/cdefs.h>
33 __RCSID("$NetBSD: icmp.c,v 1.3 2014/07/12 12:09:37 spz Exp $");
36 #include "netinet/ip.h"
37 #include "netinet/ip_icmp.h"
39 struct icmp_state
*icmp_state
;
40 static omapi_object_type_t
*dhcp_type_icmp
;
43 OMAPI_OBJECT_ALLOC (icmp_state
, struct icmp_state
, dhcp_type_icmp
)
46 trace_type_t
*trace_icmp_input
;
47 trace_type_t
*trace_icmp_output
;
50 /* Initialize the ICMP protocol. */
52 void icmp_startup (routep
, handler
)
54 void (*handler
) (struct iaddr
, u_int8_t
*, int);
56 struct protoent
*proto
;
61 /* Only initialize icmp once. */
63 log_fatal ("attempted to reinitialize icmp protocol");
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
),
70 if (result
!= ISC_R_SUCCESS
)
71 log_fatal ("Can't register icmp object type: %s",
72 isc_result_totext (result
));
74 icmp_state_allocate (&icmp_state
, MDL
);
75 icmp_state
-> icmp_handler
= handler
;
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
);
85 /* If we're playing back a trace file, don't create the socket
86 or set up the callback. */
87 if (!trace_playback ()) {
89 /* Get the protocol number (should be 1). */
90 proto
= getprotobyname ("icmp");
92 protocol
= proto
-> p_proto
;
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) {
98 log_error ("unable to create icmp socket: %m");
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");
107 /* Make sure it does routing... */
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");
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)
124 int icmp_readsocket (h
)
127 struct icmp_state
*state
;
129 state
= (struct icmp_state
*)h
;
130 return state
-> socket
;
133 int icmp_echorequest (addr
)
136 struct sockaddr_in to
;
139 #if defined (TRACING)
146 log_fatal ("ICMP protocol used before initialization.");
148 memset (&to
, 0, sizeof(to
));
150 to
.sin_len
= sizeof to
;
152 to
.sin_family
= AF_INET
;
153 to
.sin_port
= 0; /* unused. */
154 memcpy (&to
.sin_addr
, addr
-> iabuf
, sizeof to
.sin_addr
); /* XXX */
156 icmp
.icmp_type
= ICMP_ECHO
;
161 icmp
.icmp_id
= (((u_int32_t
)(u_int64_t
)addr
) ^
162 (u_int32_t
)(((u_int64_t
)addr
) >> 32));
164 icmp
.icmp_id
= (u_int32_t
)addr
;
166 memset (&icmp
.icmp_dun
, 0, sizeof icmp
.icmp_dun
);
168 icmp
.icmp_cksum
= wrapsum (checksum ((unsigned char *)&icmp
,
171 #if defined (TRACING)
172 if (trace_playback ()) {
173 char *buf
= (char *)0;
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
));
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
,
193 /* Send the ICMP packet... */
194 status
= sendto (icmp_state
-> socket
,
195 (char *)&icmp
, sizeof icmp
, 0,
196 (struct sockaddr
*)&to
, sizeof to
);
198 log_error ("icmp_echorequest %s: %m",
199 inet_ntoa(to
.sin_addr
));
201 if (status
!= sizeof icmp
)
203 #if defined (TRACING)
209 isc_result_t
icmp_echoreply (h
)
214 struct sockaddr_in from
;
215 u_int8_t icbuf
[1500];
220 struct icmp_state
*state
;
221 #if defined (TRACING)
225 state
= (struct icmp_state
*)h
;
228 status
= recvfrom (state
-> socket
, (char *)icbuf
, sizeof icbuf
, 0,
229 (struct sockaddr
*)&from
, &sl
);
231 log_error ("icmp_echoreply: %m");
232 return ISC_R_UNEXPECTED
;
235 /* Find the IP header length... */
236 ip
= (struct ip
*)icbuf
;
240 if (status
< hlen
+ (sizeof *icfrom
)) {
241 return ISC_R_SUCCESS
;
245 icfrom
= (struct icmp
*)(icbuf
+ hlen
);
247 /* Silently discard ICMP packets that aren't echoreplies. */
248 if (icfrom
-> icmp_type
!= ICMP_ECHOREPLY
) {
249 return ISC_R_SUCCESS
;
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
;
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
;
264 trace_write_packet_iov (trace_icmp_input
, 2, iov
, MDL
);
265 ia
.len
= ntohl(ia
.len
);
268 (*state
-> icmp_handler
) (ia
, icbuf
, len
);
270 return ISC_R_SUCCESS
;
273 #if defined (TRACING)
274 void trace_icmp_input_input (trace_type_t
*ttype
, unsigned length
, char *buf
)
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
));
286 void trace_icmp_input_stop (trace_type_t
*ttype
) { }
288 void trace_icmp_output_input (trace_type_t
*ttype
, unsigned length
, char *buf
)
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
)));
298 memcpy (ia
.iabuf
, buf
, 4);
300 log_error ("trace_icmp_output_input: unsent ping to %s", piaddr (ia
));
303 void trace_icmp_output_stop (trace_type_t
*ttype
) { }