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