Make setthetime() static per the prototype.
[dragonfly.git] / contrib / isc-dhcp / common / icmp.c
1 /* dhcp.c
2
3    ICMP Protocol engine - for sending out pings and receiving
4    responses. */
5
6 /*
7  * Copyright (c) 1996-2002 Internet Software Consortium.
8  * All rights reserved.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  *
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  * 3. Neither the name of The Internet Software Consortium nor the names
20  *    of its contributors may be used to endorse or promote products derived
21  *    from this software without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
24  * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
25  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
26  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
27  * DISCLAIMED.  IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
28  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
29  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
30  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
31  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
32  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
33  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
34  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35  * SUCH DAMAGE.
36  *
37  * This software has been written for the Internet Software Consortium
38  * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
39  * To learn more about the Internet Software Consortium, see
40  * ``http://www.isc.org/''.  To learn more about Vixie Enterprises,
41  * see ``http://www.vix.com''.   To learn more about Nominum, Inc., see
42  * ``http://www.nominum.com''.
43  */
44
45 #ifndef lint
46 static char copyright[] =
47 "$Id: icmp.c,v 1.30.2.5 2002/11/17 02:26:58 dhankins Exp $ Copyright (c) 1996-2002 The Internet Software Consortium.  All rights reserved.\n";
48 #endif /* not lint */
49
50 #include "dhcpd.h"
51 #include "netinet/ip.h"
52 #include "netinet/ip_icmp.h"
53
54 struct icmp_state *icmp_state;
55 static omapi_object_type_t *dhcp_type_icmp;
56 static int no_icmp;
57
58 OMAPI_OBJECT_ALLOC (icmp_state, struct icmp_state, dhcp_type_icmp)
59
60 #if defined (TRACING)
61 trace_type_t *trace_icmp_input;
62 trace_type_t *trace_icmp_output;
63 #endif
64
65 /* Initialize the ICMP protocol. */
66
67 void icmp_startup (routep, handler)
68         int routep;
69         void (*handler) PROTO ((struct iaddr, u_int8_t *, int));
70 {
71         struct protoent *proto;
72         int protocol = 1;
73         struct sockaddr_in from;
74         int fd;
75         int state;
76         struct icmp_state *new;
77         omapi_object_t *h;
78         isc_result_t result;
79
80         /* Only initialize icmp once. */
81         if (dhcp_type_icmp)
82                 log_fatal ("attempted to reinitialize icmp protocol");
83
84         result = omapi_object_type_register (&dhcp_type_icmp, "icmp",
85                                              0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
86                                              sizeof (struct icmp_state),
87                                              0, RC_MISC);
88
89         if (result != ISC_R_SUCCESS)
90                 log_fatal ("Can't register icmp object type: %s",
91                            isc_result_totext (result));
92
93         icmp_state_allocate (&icmp_state, MDL);
94         icmp_state -> icmp_handler = handler;
95
96 #if defined (TRACING)
97         trace_icmp_input = trace_type_register ("icmp-input", (void *)0,
98                                                 trace_icmp_input_input,
99                                                 trace_icmp_input_stop, MDL);
100         trace_icmp_output = trace_type_register ("icmp-output", (void *)0,
101                                                  trace_icmp_output_input,
102                                                  trace_icmp_output_stop, MDL);
103
104         /* If we're playing back a trace file, don't create the socket
105            or set up the callback. */
106         if (!trace_playback ()) {
107 #endif
108                 /* Get the protocol number (should be 1). */
109                 proto = getprotobyname ("icmp");
110                 if (proto)
111                         protocol = proto -> p_proto;
112                 
113                 /* Get a raw socket for the ICMP protocol. */
114                 icmp_state -> socket = socket (AF_INET, SOCK_RAW, protocol);
115                 if (icmp_state -> socket < 0) {
116                         no_icmp = 1;
117                         log_error ("unable to create icmp socket: %m");
118                         return;
119                 }
120
121 #if defined (HAVE_SETFD)
122                 if (fcntl (icmp_state -> socket, F_SETFD, 1) < 0)
123                         log_error ("Can't set close-on-exec on icmp: %m");
124 #endif
125
126                 /* Make sure it does routing... */
127                 state = 0;
128                 if (setsockopt (icmp_state -> socket, SOL_SOCKET, SO_DONTROUTE,
129                                 (char *)&state, sizeof state) < 0)
130                         log_fatal ("Can't disable SO_DONTROUTE on ICMP: %m");
131
132                 result = (omapi_register_io_object
133                           ((omapi_object_t *)icmp_state,
134                            icmp_readsocket, 0, icmp_echoreply, 0, 0));
135                 if (result != ISC_R_SUCCESS)
136                         log_fatal ("Can't register icmp handle: %s",
137                                    isc_result_totext (result));
138 #if defined (TRACING)
139         }
140 #endif
141 }
142
143 int icmp_readsocket (h)
144         omapi_object_t *h;
145 {
146         struct icmp_state *state;
147
148         state = (struct icmp_state *)h;
149         return state -> socket;
150 }
151
152 int icmp_echorequest (addr)
153         struct iaddr *addr;
154 {
155         struct sockaddr_in to;
156         struct icmp icmp;
157         int status;
158 #if defined (TRACING)
159         trace_iov_t iov [2];
160 #endif
161
162         if (no_icmp)
163                 return 1;
164         if (!icmp_state)
165                 log_fatal ("ICMP protocol used before initialization.");
166
167         memset (&to, 0, sizeof(to));
168 #ifdef HAVE_SA_LEN
169         to.sin_len = sizeof to;
170 #endif
171         to.sin_family = AF_INET;
172         to.sin_port = 0; /* unused. */
173         memcpy (&to.sin_addr, addr -> iabuf, sizeof to.sin_addr); /* XXX */
174
175         icmp.icmp_type = ICMP_ECHO;
176         icmp.icmp_code = 0;
177         icmp.icmp_cksum = 0;
178         icmp.icmp_seq = 0;
179 #ifdef PTRSIZE_64BIT
180         icmp.icmp_id = (((u_int32_t)(u_int64_t)addr) ^
181                         (u_int32_t)(((u_int64_t)addr) >> 32));
182 #else
183         icmp.icmp_id = (u_int32_t)addr;
184 #endif
185         memset (&icmp.icmp_dun, 0, sizeof icmp.icmp_dun);
186
187         icmp.icmp_cksum = wrapsum (checksum ((unsigned char *)&icmp,
188                                              sizeof icmp, 0));
189
190 #if defined (TRACING)
191         if (trace_playback ()) {
192                 char *buf = (char *)0;
193                 unsigned buflen = 0;
194
195                 /* Consume the ICMP event. */
196                 status = trace_get_packet (&trace_icmp_output, &buflen, &buf);
197                 if (status != ISC_R_SUCCESS)
198                         log_error ("icmp_echorequest: %s",
199                                    isc_result_totext (status));
200                 if (buf)
201                         dfree (buf, MDL);
202         } else {
203                 if (trace_record ()) {
204                         iov [0].buf = (char *)addr;
205                         iov [0].len = sizeof *addr;
206                         iov [1].buf = (char *)&icmp;
207                         iov [1].len = sizeof icmp;
208                         trace_write_packet_iov (trace_icmp_output,
209                                                 2, iov, MDL);
210                 }
211 #endif
212                 /* Send the ICMP packet... */
213                 status = sendto (icmp_state -> socket,
214                                  (char *)&icmp, sizeof icmp, 0,
215                                  (struct sockaddr *)&to, sizeof to);
216                 if (status < 0)
217                         log_error ("icmp_echorequest %s: %m",
218                                    inet_ntoa(to.sin_addr));
219
220                 if (status != sizeof icmp)
221                         return 0;
222 #if defined (TRACING)
223         }
224 #endif
225         return 1;
226 }
227
228 isc_result_t icmp_echoreply (h)
229         omapi_object_t *h;
230 {
231         struct icmp *icfrom;
232         struct ip *ip;
233         struct sockaddr_in from;
234         u_int8_t icbuf [1500];
235         int status;
236         SOCKLEN_T sl;
237         int hlen, len;
238         struct iaddr ia;
239         struct icmp_state *state;
240 #if defined (TRACING)
241         trace_iov_t iov [2];
242 #endif
243
244         state = (struct icmp_state *)h;
245
246         sl = sizeof from;
247         status = recvfrom (state -> socket, (char *)icbuf, sizeof icbuf, 0,
248                           (struct sockaddr *)&from, &sl);
249         if (status < 0) {
250                 log_error ("icmp_echoreply: %m");
251                 return ISC_R_UNEXPECTED;
252         }
253
254         /* Find the IP header length... */
255         ip = (struct ip *)icbuf;
256         hlen = IP_HL (ip);
257
258         /* Short packet? */
259         if (status < hlen + (sizeof *icfrom)) {
260                 return ISC_R_SUCCESS;
261         }
262
263         len = status - hlen;
264         icfrom = (struct icmp *)(icbuf + hlen);
265
266         /* Silently discard ICMP packets that aren't echoreplies. */
267         if (icfrom -> icmp_type != ICMP_ECHOREPLY) {
268                 return ISC_R_SUCCESS;
269         }
270
271         /* If we were given a second-stage handler, call it. */
272         if (state -> icmp_handler) {
273                 memcpy (ia.iabuf, &from.sin_addr, sizeof from.sin_addr);
274                 ia.len = sizeof from.sin_addr;
275
276 #if defined (TRACING)
277                 if (trace_record ()) {
278                         ia.len = htonl(ia.len);
279                         iov [0].buf = (char *)&ia;
280                         iov [0].len = sizeof ia;
281                         iov [1].buf = (char *)icbuf;
282                         iov [1].len = len;
283                         trace_write_packet_iov (trace_icmp_input, 2, iov, MDL);
284                         ia.len = ntohl(ia.len);
285                 }
286 #endif
287                 (*state -> icmp_handler) (ia, icbuf, len);
288         }
289         return ISC_R_SUCCESS;
290 }
291
292 #if defined (TRACING)
293 void trace_icmp_input_input (trace_type_t *ttype, unsigned length, char *buf)
294 {
295         struct iaddr *ia;
296         unsigned len;
297         u_int8_t *icbuf;
298         ia = (struct iaddr *)buf;
299         ia->len = ntohl(ia->len);
300         icbuf = (u_int8_t *)(ia + 1);
301         if (icmp_state -> icmp_handler)
302                 (*icmp_state -> icmp_handler) (*ia, icbuf,
303                                                (int)(length - sizeof ia));
304 }
305
306 void trace_icmp_input_stop (trace_type_t *ttype) { }
307
308 void trace_icmp_output_input (trace_type_t *ttype, unsigned length, char *buf)
309 {
310         struct icmp *icmp;
311         struct iaddr ia;
312
313         if (length != (sizeof (*icmp) + (sizeof ia))) {
314                 log_error ("trace_icmp_output_input: data size mismatch %d:%d",
315                            length, (int)((sizeof (*icmp)) + (sizeof ia)));
316                 return;
317         }
318         ia.len = 4;
319         memcpy (ia.iabuf, buf, 4);
320         icmp = (struct icmp *)(buf + 1);
321
322         log_error ("trace_icmp_output_input: unsent ping to %s", piaddr (ia));
323 }
324
325 void trace_icmp_output_stop (trace_type_t *ttype) { }
326 #endif /* TRACING */