common/icmp.c

Go to the documentation of this file.
00001 /* dhcp.c
00002 
00003    ICMP Protocol engine - for sending out pings and receiving
00004    responses. */
00005 
00006 /*
00007  * Copyright (c) 2011,2013,2014 by Internet Systems Consortium, Inc. ("ISC")
00008  * Copyright (c) 2004,2007,2009 by Internet Systems Consortium, Inc. ("ISC")
00009  * Copyright (c) 1996-2003 by Internet Software Consortium
00010  *
00011  * Permission to use, copy, modify, and distribute this software for any
00012  * purpose with or without fee is hereby granted, provided that the above
00013  * copyright notice and this permission notice appear in all copies.
00014  *
00015  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
00016  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
00017  * MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR
00018  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
00019  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
00020  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
00021  * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
00022  *
00023  *   Internet Systems Consortium, Inc.
00024  *   950 Charter Street
00025  *   Redwood City, CA 94063
00026  *   <info@isc.org>
00027  *   https://www.isc.org/
00028  *
00029  */
00030 
00031 #include "dhcpd.h"
00032 #include "netinet/ip.h"
00033 #include "netinet/ip_icmp.h"
00034 
00035 struct icmp_state *icmp_state;
00036 static omapi_object_type_t *dhcp_type_icmp;
00037 static int no_icmp;
00038 
00039 OMAPI_OBJECT_ALLOC (icmp_state, struct icmp_state, dhcp_type_icmp)
00040 
00041 #if defined (TRACING)
00042 trace_type_t *trace_icmp_input;
00043 trace_type_t *trace_icmp_output;
00044 #endif
00045 
00046 /* Initialize the ICMP protocol. */
00047 
00048 void icmp_startup (routep, handler)
00049         int routep;
00050         void (*handler) (struct iaddr, u_int8_t *, int);
00051 {
00052         struct protoent *proto;
00053         int protocol = 1;
00054         int state;
00055         isc_result_t result;
00056 
00057         /* Only initialize icmp once. */
00058         if (dhcp_type_icmp)
00059                 log_fatal ("attempted to reinitialize icmp protocol");
00060 
00061         result = omapi_object_type_register (&dhcp_type_icmp, "icmp",
00062                                              0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
00063                                              sizeof (struct icmp_state),
00064                                              0, RC_MISC);
00065 
00066         if (result != ISC_R_SUCCESS)
00067                 log_fatal ("Can't register icmp object type: %s",
00068                            isc_result_totext (result));
00069 
00070         icmp_state_allocate (&icmp_state, MDL);
00071         icmp_state -> icmp_handler = handler;
00072 
00073 #if defined (TRACING)
00074         trace_icmp_input = trace_type_register ("icmp-input", (void *)0,
00075                                                 trace_icmp_input_input,
00076                                                 trace_icmp_input_stop, MDL);
00077         trace_icmp_output = trace_type_register ("icmp-output", (void *)0,
00078                                                  trace_icmp_output_input,
00079                                                  trace_icmp_output_stop, MDL);
00080 
00081         /* If we're playing back a trace file, don't create the socket
00082            or set up the callback. */
00083         if (!trace_playback ()) {
00084 #endif
00085                 /* Get the protocol number (should be 1). */
00086                 proto = getprotobyname ("icmp");
00087                 if (proto)
00088                         protocol = proto -> p_proto;
00089                 
00090                 /* Get a raw socket for the ICMP protocol. */
00091                 icmp_state -> socket = socket (AF_INET, SOCK_RAW, protocol);
00092                 if (icmp_state -> socket < 0) {
00093                         no_icmp = 1;
00094                         log_error ("unable to create icmp socket: %m");
00095                         return;
00096                 }
00097 
00098 #if defined (HAVE_SETFD)
00099                 if (fcntl (icmp_state -> socket, F_SETFD, 1) < 0)
00100                         log_error ("Can't set close-on-exec on icmp: %m");
00101 #endif
00102 
00103                 /* Make sure it does routing... */
00104                 state = 0;
00105                 if (setsockopt (icmp_state -> socket, SOL_SOCKET, SO_DONTROUTE,
00106                                 (char *)&state, sizeof state) < 0)
00107                         log_fatal ("Can't disable SO_DONTROUTE on ICMP: %m");
00108 
00109                 result = (omapi_register_io_object
00110                           ((omapi_object_t *)icmp_state,
00111                            icmp_readsocket, 0, icmp_echoreply, 0, 0));
00112                 if (result != ISC_R_SUCCESS)
00113                         log_fatal ("Can't register icmp handle: %s",
00114                                    isc_result_totext (result));
00115 #if defined (TRACING)
00116         }
00117 #endif
00118 }
00119 
00120 int icmp_readsocket (h)
00121         omapi_object_t *h;
00122 {
00123         struct icmp_state *state;
00124 
00125         state = (struct icmp_state *)h;
00126         return state -> socket;
00127 }
00128 
00129 int icmp_echorequest (addr)
00130         struct iaddr *addr;
00131 {
00132         struct sockaddr_in to;
00133         struct icmp icmp;
00134         int status;
00135 #if defined (TRACING)
00136         trace_iov_t iov [2];
00137 #endif
00138 
00139         if (no_icmp)
00140                 return 1;
00141         if (!icmp_state)
00142                 log_fatal ("ICMP protocol used before initialization.");
00143 
00144         memset (&to, 0, sizeof(to));
00145 #ifdef HAVE_SA_LEN
00146         to.sin_len = sizeof to;
00147 #endif
00148         to.sin_family = AF_INET;
00149         to.sin_port = 0; /* unused. */
00150         memcpy (&to.sin_addr, addr -> iabuf, sizeof to.sin_addr); /* XXX */
00151 
00152         icmp.icmp_type = ICMP_ECHO;
00153         icmp.icmp_code = 0;
00154         icmp.icmp_cksum = 0;
00155         icmp.icmp_seq = 0;
00156 #if SIZEOF_STRUCT_IADDR_P == 8
00157         icmp.icmp_id = (((u_int32_t)(u_int64_t)addr) ^
00158                         (u_int32_t)(((u_int64_t)addr) >> 32));
00159 #else
00160         icmp.icmp_id = (u_int32_t)addr;
00161 #endif
00162         memset (&icmp.icmp_dun, 0, sizeof icmp.icmp_dun);
00163 
00164         icmp.icmp_cksum = wrapsum (checksum ((unsigned char *)&icmp,
00165                                              sizeof icmp, 0));
00166 
00167 #if defined (TRACING)
00168         if (trace_playback ()) {
00169                 char *buf = (char *)0;
00170                 unsigned buflen = 0;
00171 
00172                 /* Consume the ICMP event. */
00173                 status = trace_get_packet (&trace_icmp_output, &buflen, &buf);
00174                 if (status != ISC_R_SUCCESS)
00175                         log_error ("icmp_echorequest: %s",
00176                                    isc_result_totext (status));
00177                 if (buf)
00178                         dfree (buf, MDL);
00179         } else {
00180                 if (trace_record ()) {
00181                         iov [0].buf = (char *)addr;
00182                         iov [0].len = sizeof *addr;
00183                         iov [1].buf = (char *)&icmp;
00184                         iov [1].len = sizeof icmp;
00185                         trace_write_packet_iov (trace_icmp_output,
00186                                                 2, iov, MDL);
00187                 }
00188 #endif
00189                 /* Send the ICMP packet... */
00190                 status = sendto (icmp_state -> socket,
00191                                  (char *)&icmp, sizeof icmp, 0,
00192                                  (struct sockaddr *)&to, sizeof to);
00193                 if (status < 0)
00194                         log_error ("icmp_echorequest %s: %m",
00195                                    inet_ntoa(to.sin_addr));
00196 
00197                 if (status != sizeof icmp)
00198                         return 0;
00199 #if defined (TRACING)
00200         }
00201 #endif
00202         return 1;
00203 }
00204 
00205 isc_result_t icmp_echoreply (h)
00206         omapi_object_t *h;
00207 {
00208         struct icmp *icfrom;
00209         struct ip *ip;
00210         struct sockaddr_in from;
00211         u_int8_t icbuf [1500];
00212         int status;
00213         SOCKLEN_T sl;
00214         int hlen, len;
00215         struct iaddr ia;
00216         struct icmp_state *state;
00217 #if defined (TRACING)
00218         trace_iov_t iov [2];
00219 #endif
00220 
00221         state = (struct icmp_state *)h;
00222 
00223         sl = sizeof from;
00224         status = recvfrom (state -> socket, (char *)icbuf, sizeof icbuf, 0,
00225                           (struct sockaddr *)&from, &sl);
00226         if (status < 0) {
00227                 log_error ("icmp_echoreply: %m");
00228                 return ISC_R_UNEXPECTED;
00229         }
00230 
00231         /* Find the IP header length... */
00232         ip = (struct ip *)icbuf;
00233         hlen = IP_HL (ip);
00234 
00235         /* Short packet? */
00236         if (status < hlen + (sizeof *icfrom)) {
00237                 return ISC_R_SUCCESS;
00238         }
00239 
00240         len = status - hlen;
00241         icfrom = (struct icmp *)(icbuf + hlen);
00242 
00243         /* Silently discard ICMP packets that aren't echoreplies. */
00244         if (icfrom -> icmp_type != ICMP_ECHOREPLY) {
00245                 return ISC_R_SUCCESS;
00246         }
00247 
00248         /* If we were given a second-stage handler, call it. */
00249         if (state -> icmp_handler) {
00250                 memcpy (ia.iabuf, &from.sin_addr, sizeof from.sin_addr);
00251                 ia.len = sizeof from.sin_addr;
00252 
00253 #if defined (TRACING)
00254                 if (trace_record ()) {
00255                         ia.len = htonl(ia.len);
00256                         iov [0].buf = (char *)&ia;
00257                         iov [0].len = sizeof ia;
00258                         iov [1].buf = (char *)icbuf;
00259                         iov [1].len = len;
00260                         trace_write_packet_iov (trace_icmp_input, 2, iov, MDL);
00261                         ia.len = ntohl(ia.len);
00262                 }
00263 #endif
00264                 (*state -> icmp_handler) (ia, icbuf, len);
00265         }
00266         return ISC_R_SUCCESS;
00267 }
00268 
00269 #if defined (TRACING)
00270 void trace_icmp_input_input (trace_type_t *ttype, unsigned length, char *buf)
00271 {
00272         struct iaddr *ia;
00273         u_int8_t *icbuf;
00274         ia = (struct iaddr *)buf;
00275         ia->len = ntohl(ia->len);
00276         icbuf = (u_int8_t *)(ia + 1);
00277         if (icmp_state -> icmp_handler)
00278                 (*icmp_state -> icmp_handler) (*ia, icbuf,
00279                                                (int)(length - sizeof ia));
00280 }
00281 
00282 void trace_icmp_input_stop (trace_type_t *ttype) { }
00283 
00284 void trace_icmp_output_input (trace_type_t *ttype, unsigned length, char *buf)
00285 {
00286         struct iaddr ia;
00287 
00288         if (length != (sizeof (struct icmp) + sizeof (ia))) {
00289                 log_error ("trace_icmp_output_input: data size mismatch %d:%d",
00290                            length, (int)(sizeof (struct icmp) + sizeof (ia)));
00291                 return;
00292         }
00293         ia.len = 4;
00294         memcpy (ia.iabuf, buf, 4);
00295 
00296         log_error ("trace_icmp_output_input: unsent ping to %s", piaddr (ia));
00297 }
00298 
00299 void trace_icmp_output_stop (trace_type_t *ttype) { }
00300 #endif /* TRACING */

Generated on 5 Apr 2014 for ISC DHCP by  doxygen 1.6.1