xref: /onnv-gate/usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/icmp.c (revision 0:68f95e015346)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright (c) 1993-2001 by Sun Microsystems, Inc.
24  * All rights reserved.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <unistd.h>
32 #include <stdarg.h>
33 #include <sys/types.h>
34 #include <fcntl.h>
35 #include <errno.h>
36 #include <syslog.h>
37 #include <string.h>
38 #include <time.h>
39 #include <thread.h>
40 #include <sys/socket.h>
41 #include <net/if.h>
42 #include <netinet/in_systm.h>
43 #include <netinet/in.h>
44 #include <arpa/inet.h>
45 #include <netinet/ip.h>
46 #include <netinet/ip_icmp.h>
47 #include <netinet/udp.h>
48 #include <netinet/dhcp.h>
49 #include "dhcpd.h"
50 #include "per_dnet.h"
51 #include "interfaces.h"
52 #include <v4_sum_impl.h>
53 #include <locale.h>
54 
55 #define	ICMP_ECHO_SIZE	(sizeof (struct icmp) + 36)
56 
57 /*
58  * An implementation of ICMP ECHO for use in detecting addresses already
59  * in use. Address argument expected in network order. Result is set to
60  * B_TRUE if a ICMP ECHO reply is received, B_FALSE if not. Returns 0 if
61  * no errors were encountered, nonzero otherwise.
62  *
63  * NOTES: Not interface specific. We use our routing tables to route the
64  * messages correctly, and collect responses. This may mean that we
65  * receive an ICMP ECHO reply thru an interface the daemon has not been
66  * directed to watch. However, I believe that *ANY* echo reply means
67  * trouble, regardless of the route taken!
68  *
69  * 'cip' is expected in network order.
70  */
71 
72 int
icmp_echo_check(struct in_addr * cip,boolean_t * result)73 icmp_echo_check(struct in_addr *cip, boolean_t	*result)
74 {
75 	struct icmp 		*icp;
76 	struct ip		*ipp;
77 	int			sequence = 0, i, s, s_cnt, r_cnt,
78 				icmp_identifier, error = 0;
79 	socklen_t		fromlen;
80 	ushort_t		ip_hlen;
81 	hrtime_t		recv_intrvl;
82 	struct sockaddr_in	to, from;
83 	struct pollfd		pfd;
84 	char			ntoab[INET_ADDRSTRLEN];
85 	ulong_t			outpack[DHCP_SCRATCH/sizeof (ulong_t)];
86 	ulong_t			inpack[DHCP_SCRATCH/sizeof (ulong_t)];
87 
88 	*result = B_FALSE;
89 
90 	(void) inet_ntop(AF_INET, cip, ntoab, sizeof (ntoab));
91 
92 	if ((s = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) < 0) {
93 		error = errno;
94 		dhcpmsg(LOG_ERR,
95 		    "Error opening raw socket for ICMP (ping %s).\n", ntoab);
96 		return (error);
97 	}
98 
99 	if (fcntl(s, F_SETFL, O_NDELAY) == -1) {
100 		error = errno;
101 		dhcpmsg(LOG_ERR,
102 		    "Error setting ICMP socket to no delay. (ping %s)\n",
103 		    ntoab);
104 		(void) close(s);
105 		return (error);
106 	}
107 
108 	pfd.fd = s;
109 	pfd.events = POLLIN | POLLPRI;
110 	pfd.revents = 0;
111 
112 	icmp_identifier = (int)thr_self() & (ushort_t)-1;
113 	(void) memset((void *)outpack, 0, sizeof (outpack));
114 	outpack[10] = 0x12345678;
115 	icp = (struct icmp *)outpack;
116 	icp->icmp_code = 0;
117 	icp->icmp_type = ICMP_ECHO;
118 	icp->icmp_id = icmp_identifier;
119 
120 	(void) memset((void *)&to, 0, sizeof (struct sockaddr_in));
121 	to.sin_family = AF_INET;
122 	to.sin_addr.s_addr = cip->s_addr;
123 
124 	/*
125 	 * We make icmp_tries attempts to contact the target. We
126 	 * wait the same length of time for a response in both cases.
127 	 */
128 	for (i = 0; i < icmp_tries; i++) {
129 		icp->icmp_seq = sequence++;
130 		icp->icmp_cksum = 0;
131 		icp->icmp_cksum = ipv4cksum((uint16_t *)icp, ICMP_ECHO_SIZE);
132 
133 		/* Deliver our ECHO. */
134 		s_cnt = sendto(s, (char *)outpack, ICMP_ECHO_SIZE, 0,
135 		    (struct sockaddr *)&to, sizeof (struct sockaddr));
136 
137 		if (s_cnt < 0 || s_cnt != ICMP_ECHO_SIZE) {
138 			error = errno;
139 			dhcpmsg(LOG_ERR,
140 			    "Error sending ICMP message. (ping %s).\n",
141 			    ntoab);
142 			(void) close(s);
143 			return (error);
144 		}
145 
146 		/* Collect replies. */
147 		recv_intrvl = gethrtime() +
148 		    (hrtime_t)(icmp_timeout) * 1000000;
149 
150 		while (gethrtime() < recv_intrvl) {
151 			if (poll(&pfd, (nfds_t)1, icmp_timeout) < 0 ||
152 			    pfd.revents & (POLLERR | POLLHUP | POLLNVAL)) {
153 				/* EINTR is masked  - must be serious */
154 				error = errno;
155 				dhcpmsg(LOG_ERR, "Poll: ICMP reply for %s.\n",
156 				    ntoab);
157 				(void) close(s);
158 				return (error);
159 			}
160 
161 			if (!pfd.revents) {
162 				continue;	/* no data, timeout */
163 			}
164 
165 			fromlen = sizeof (from);
166 			if ((r_cnt = recvfrom(s, (char *)inpack,
167 			    sizeof (inpack), 0, (struct sockaddr *)&from,
168 			    &fromlen)) < 0) {
169 				error = errno;
170 				if (error == EAGAIN) {
171 					error = 0;
172 					continue;
173 				}
174 				/* EINTR is masked  - must be serious */
175 				dhcpmsg(LOG_ERR,
176 				    "recvfrom: ICMP reply for %s.\n",
177 				    ntoab);
178 				(void) close(s);
179 				return (error);
180 			}
181 
182 			if (from.sin_addr.s_addr != cip->s_addr)
183 				continue; /* Not from the IP of interest */
184 			/*
185 			 * We know we got an ICMP message of some type from
186 			 * the IP of interest. Be conservative and
187 			 * consider it in use. The following logic is just
188 			 * for identifying problems in the response.
189 			 */
190 			*result = B_TRUE;
191 
192 			if (!debug)
193 				break;
194 
195 			ipp = (struct ip *)inpack;
196 			if (r_cnt != ntohs(ipp->ip_len)) {
197 				/* bogus IP header */
198 				dhcpmsg(LOG_NOTICE, "Malformed ICMP message "
199 				    "received from host %s: len %d != %d\n",
200 				    ntoab, r_cnt, ntohs(ipp->ip_len));
201 				break;
202 			}
203 			ip_hlen = ipp->ip_hl << 2;
204 			if (r_cnt < (int)(ip_hlen + ICMP_MINLEN)) {
205 				dhcpmsg(LOG_NOTICE, "ICMP message received "
206 				    "from host %s is too small.\n", ntoab);
207 				break;
208 			}
209 			icp = (struct icmp *)((uint_t)inpack + ip_hlen);
210 			if (ipv4cksum((uint16_t *)icp,
211 			    ntohs(ipp->ip_len) - ip_hlen) != 0) {
212 				dhcpmsg(LOG_NOTICE, "Bad checksum on incoming "
213 				    "ICMP echo reply. (ping %s)\n", ntoab);
214 			}
215 			if (icp->icmp_type != ICMP_ECHOREPLY) {
216 				dhcpmsg(LOG_NOTICE,
217 				    "Unexpected ICMP type %d from %s.\n",
218 				    icp->icmp_type, ntoab);
219 			}
220 			if (icp->icmp_id != icmp_identifier) {
221 				dhcpmsg(LOG_NOTICE,
222 				    "ICMP message id mismatch (from %s).\n",
223 				    ntoab);
224 			}
225 			if (icp->icmp_seq != (sequence - 1)) {
226 				dhcpmsg(LOG_NOTICE, "ICMP sequence mismatch: "
227 				    "%d != %d (ping %s)\n", icp->icmp_seq,
228 				    sequence - 1, ntoab);
229 			}
230 			break;
231 		}
232 	}
233 	(void) close(s);
234 
235 	return (error);
236 }
237