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 }