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
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 }