3 ICMP Protocol engine - for sending out pings and receiving
7 * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
8 * Copyright (c) 1996-2003 by Internet Software Consortium
10 * Permission to use, copy, modify, and distribute this software for any
11 * purpose with or without fee is hereby granted, provided that the above
12 * copyright notice and this permission notice appear in all copies.
14 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
15 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
16 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
17 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
18 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
19 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
20 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22 * Internet Systems Consortium, Inc.
24 * Redwood City, CA 94063
28 * This software has been written for Internet Systems Consortium
29 * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
30 * To learn more about Internet Systems Consortium, see
31 * ``http://www.isc.org/''. To learn more about Vixie Enterprises,
32 * see ``http://www.vix.com''. To learn more about Nominum, Inc., see
33 * ``http://www.nominum.com''.
37 static char copyright
[] =
38 "$Id: icmp.c,v 1.6 2005/08/11 17:13:21 drochner Exp $ Copyright (c) 2004 Internet Systems Consortium. All rights reserved.\n";
42 #include "netinet/ip.h"
43 #include "netinet/ip_icmp.h"
45 struct icmp_state
*icmp_state
;
46 static omapi_object_type_t
*dhcp_type_icmp
;
49 OMAPI_OBJECT_ALLOC (icmp_state
, struct icmp_state
, dhcp_type_icmp
)
52 trace_type_t
*trace_icmp_input
;
53 trace_type_t
*trace_icmp_output
;
56 /* Initialize the ICMP protocol. */
58 void icmp_startup (routep
, handler
)
60 void (*handler
) PROTO ((struct iaddr
, u_int8_t
*, int));
62 struct protoent
*proto
;
67 /* Only initialize icmp once. */
69 log_fatal ("attempted to reinitialize icmp protocol");
71 result
= omapi_object_type_register (&dhcp_type_icmp
, "icmp",
72 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
73 sizeof (struct icmp_state
),
76 if (result
!= ISC_R_SUCCESS
)
77 log_fatal ("Can't register icmp object type: %s",
78 isc_result_totext (result
));
80 icmp_state_allocate (&icmp_state
, MDL
);
81 icmp_state
-> icmp_handler
= handler
;
84 trace_icmp_input
= trace_type_register ("icmp-input", (void *)0,
85 trace_icmp_input_input
,
86 trace_icmp_input_stop
, MDL
);
87 trace_icmp_output
= trace_type_register ("icmp-output", (void *)0,
88 trace_icmp_output_input
,
89 trace_icmp_output_stop
, MDL
);
91 /* If we're playing back a trace file, don't create the socket
92 or set up the callback. */
93 if (!trace_playback ()) {
95 /* Get the protocol number (should be 1). */
96 proto
= getprotobyname ("icmp");
98 protocol
= proto
-> p_proto
;
100 /* Get a raw socket for the ICMP protocol. */
101 icmp_state
-> socket
= socket (AF_INET
, SOCK_RAW
, protocol
);
102 if (icmp_state
-> socket
< 0) {
104 log_error ("unable to create icmp socket: %m");
108 #if defined (HAVE_SETFD)
109 if (fcntl (icmp_state
-> socket
, F_SETFD
, 1) < 0)
110 log_error ("Can't set close-on-exec on icmp: %m");
113 /* Make sure it does routing... */
115 if (setsockopt (icmp_state
-> socket
, SOL_SOCKET
, SO_DONTROUTE
,
116 (char *)&state
, sizeof state
) < 0)
117 log_fatal ("Can't disable SO_DONTROUTE on ICMP: %m");
119 result
= (omapi_register_io_object
120 ((omapi_object_t
*)icmp_state
,
121 icmp_readsocket
, 0, icmp_echoreply
, 0, 0));
122 if (result
!= ISC_R_SUCCESS
)
123 log_fatal ("Can't register icmp handle: %s",
124 isc_result_totext (result
));
125 #if defined (TRACING)
130 int icmp_readsocket (h
)
133 struct icmp_state
*state
;
135 state
= (struct icmp_state
*)h
;
136 return state
-> socket
;
139 int icmp_echorequest (addr
)
142 struct sockaddr_in to
;
145 #if defined (TRACING)
152 log_fatal ("ICMP protocol used before initialization.");
154 memset (&to
, 0, sizeof(to
));
156 to
.sin_len
= sizeof to
;
158 to
.sin_family
= AF_INET
;
159 to
.sin_port
= 0; /* unused. */
160 memcpy (&to
.sin_addr
, addr
-> iabuf
, sizeof to
.sin_addr
); /* XXX */
162 icmp
.icmp_type
= ICMP_ECHO
;
167 icmp
.icmp_id
= (((u_int32_t
)(u_int64_t
)addr
) ^
168 (u_int32_t
)(((u_int64_t
)addr
) >> 32));
170 icmp
.icmp_id
= (u_int32_t
)addr
;
172 memset (&icmp
.icmp_dun
, 0, sizeof icmp
.icmp_dun
);
174 icmp
.icmp_cksum
= wrapsum (checksum ((unsigned char *)&icmp
,
177 #if defined (TRACING)
178 if (trace_playback ()) {
179 char *buf
= (char *)0;
182 /* Consume the ICMP event. */
183 status
= trace_get_packet (&trace_icmp_output
, &buflen
, &buf
);
184 if (status
!= ISC_R_SUCCESS
)
185 log_error ("icmp_echorequest: %s",
186 isc_result_totext (status
));
190 if (trace_record ()) {
191 iov
[0].buf
= (char *)addr
;
192 iov
[0].len
= sizeof *addr
;
193 iov
[1].buf
= (char *)&icmp
;
194 iov
[1].len
= sizeof icmp
;
195 trace_write_packet_iov (trace_icmp_output
,
199 /* Send the ICMP packet... */
200 status
= sendto (icmp_state
-> socket
,
201 (char *)&icmp
, sizeof icmp
, 0,
202 (struct sockaddr
*)&to
, sizeof to
);
204 log_error ("icmp_echorequest %s: %m",
205 inet_ntoa(to
.sin_addr
));
207 if (status
!= sizeof icmp
)
209 #if defined (TRACING)
215 isc_result_t
icmp_echoreply (h
)
220 struct sockaddr_in from
;
221 u_int8_t icbuf
[1500];
226 struct icmp_state
*state
;
227 #if defined (TRACING)
231 state
= (struct icmp_state
*)h
;
234 status
= recvfrom (state
-> socket
, (char *)icbuf
, sizeof icbuf
, 0,
235 (struct sockaddr
*)&from
, &sl
);
237 log_error ("icmp_echoreply: %m");
238 return ISC_R_UNEXPECTED
;
241 /* Find the IP header length... */
242 ip
= (struct ip
*)icbuf
;
246 if (status
< hlen
+ (sizeof *icfrom
)) {
247 return ISC_R_SUCCESS
;
251 icfrom
= (struct icmp
*)(icbuf
+ hlen
);
253 /* Silently discard ICMP packets that aren't echoreplies. */
254 if (icfrom
-> icmp_type
!= ICMP_ECHOREPLY
) {
255 return ISC_R_SUCCESS
;
258 /* If we were given a second-stage handler, call it. */
259 if (state
-> icmp_handler
) {
260 memcpy (ia
.iabuf
, &from
.sin_addr
, sizeof from
.sin_addr
);
261 ia
.len
= sizeof from
.sin_addr
;
263 #if defined (TRACING)
264 if (trace_record ()) {
265 ia
.len
= htonl(ia
.len
);
266 iov
[0].buf
= (char *)&ia
;
267 iov
[0].len
= sizeof ia
;
268 iov
[1].buf
= (char *)icbuf
;
270 trace_write_packet_iov (trace_icmp_input
, 2, iov
, MDL
);
271 ia
.len
= ntohl(ia
.len
);
274 (*state
-> icmp_handler
) (ia
, icbuf
, len
);
276 return ISC_R_SUCCESS
;
279 #if defined (TRACING)
280 void trace_icmp_input_input (trace_type_t
*ttype
, unsigned length
, char *buf
)
284 ia
= (struct iaddr
*)buf
;
285 ia
->len
= ntohl(ia
->len
);
286 icbuf
= (u_int8_t
*)(ia
+ 1);
287 if (icmp_state
-> icmp_handler
)
288 (*icmp_state
-> icmp_handler
) (*ia
, icbuf
,
289 (int)(length
- sizeof ia
));
292 void trace_icmp_input_stop (trace_type_t
*ttype
) { }
294 void trace_icmp_output_input (trace_type_t
*ttype
, unsigned length
, char *buf
)
299 if (length
!= (sizeof (*icmp
) + (sizeof ia
))) {
300 log_error ("trace_icmp_output_input: data size mismatch %d:%d",
301 length
, (int)((sizeof (*icmp
)) + (sizeof ia
)));
305 memcpy (ia
.iabuf
, buf
, 4);
306 icmp
= (struct icmp
*)(buf
+ 1);
308 log_error ("trace_icmp_output_input: unsent ping to %s", piaddr (ia
));
311 void trace_icmp_output_stop (trace_type_t
*ttype
) { }