/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 1993-2001 by Sun Microsystems, Inc. * All rights reserved. */ #pragma ident "%Z%%M% %I% %E% SMI" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "dhcpd.h" #include "per_dnet.h" #include "interfaces.h" #include #include #define ICMP_ECHO_SIZE (sizeof (struct icmp) + 36) /* * An implementation of ICMP ECHO for use in detecting addresses already * in use. Address argument expected in network order. Result is set to * B_TRUE if a ICMP ECHO reply is received, B_FALSE if not. Returns 0 if * no errors were encountered, nonzero otherwise. * * NOTES: Not interface specific. We use our routing tables to route the * messages correctly, and collect responses. This may mean that we * receive an ICMP ECHO reply thru an interface the daemon has not been * directed to watch. However, I believe that *ANY* echo reply means * trouble, regardless of the route taken! * * 'cip' is expected in network order. */ int icmp_echo_check(struct in_addr *cip, boolean_t *result) { struct icmp *icp; struct ip *ipp; int sequence = 0, i, s, s_cnt, r_cnt, icmp_identifier, error = 0; socklen_t fromlen; ushort_t ip_hlen; hrtime_t recv_intrvl; struct sockaddr_in to, from; struct pollfd pfd; char ntoab[INET_ADDRSTRLEN]; ulong_t outpack[DHCP_SCRATCH/sizeof (ulong_t)]; ulong_t inpack[DHCP_SCRATCH/sizeof (ulong_t)]; *result = B_FALSE; (void) inet_ntop(AF_INET, cip, ntoab, sizeof (ntoab)); if ((s = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) < 0) { error = errno; dhcpmsg(LOG_ERR, "Error opening raw socket for ICMP (ping %s).\n", ntoab); return (error); } if (fcntl(s, F_SETFL, O_NDELAY) == -1) { error = errno; dhcpmsg(LOG_ERR, "Error setting ICMP socket to no delay. (ping %s)\n", ntoab); (void) close(s); return (error); } pfd.fd = s; pfd.events = POLLIN | POLLPRI; pfd.revents = 0; icmp_identifier = (int)thr_self() & (ushort_t)-1; (void) memset((void *)outpack, 0, sizeof (outpack)); outpack[10] = 0x12345678; icp = (struct icmp *)outpack; icp->icmp_code = 0; icp->icmp_type = ICMP_ECHO; icp->icmp_id = icmp_identifier; (void) memset((void *)&to, 0, sizeof (struct sockaddr_in)); to.sin_family = AF_INET; to.sin_addr.s_addr = cip->s_addr; /* * We make icmp_tries attempts to contact the target. We * wait the same length of time for a response in both cases. */ for (i = 0; i < icmp_tries; i++) { icp->icmp_seq = sequence++; icp->icmp_cksum = 0; icp->icmp_cksum = ipv4cksum((uint16_t *)icp, ICMP_ECHO_SIZE); /* Deliver our ECHO. */ s_cnt = sendto(s, (char *)outpack, ICMP_ECHO_SIZE, 0, (struct sockaddr *)&to, sizeof (struct sockaddr)); if (s_cnt < 0 || s_cnt != ICMP_ECHO_SIZE) { error = errno; dhcpmsg(LOG_ERR, "Error sending ICMP message. (ping %s).\n", ntoab); (void) close(s); return (error); } /* Collect replies. */ recv_intrvl = gethrtime() + (hrtime_t)(icmp_timeout) * 1000000; while (gethrtime() < recv_intrvl) { if (poll(&pfd, (nfds_t)1, icmp_timeout) < 0 || pfd.revents & (POLLERR | POLLHUP | POLLNVAL)) { /* EINTR is masked - must be serious */ error = errno; dhcpmsg(LOG_ERR, "Poll: ICMP reply for %s.\n", ntoab); (void) close(s); return (error); } if (!pfd.revents) { continue; /* no data, timeout */ } fromlen = sizeof (from); if ((r_cnt = recvfrom(s, (char *)inpack, sizeof (inpack), 0, (struct sockaddr *)&from, &fromlen)) < 0) { error = errno; if (error == EAGAIN) { error = 0; continue; } /* EINTR is masked - must be serious */ dhcpmsg(LOG_ERR, "recvfrom: ICMP reply for %s.\n", ntoab); (void) close(s); return (error); } if (from.sin_addr.s_addr != cip->s_addr) continue; /* Not from the IP of interest */ /* * We know we got an ICMP message of some type from * the IP of interest. Be conservative and * consider it in use. The following logic is just * for identifying problems in the response. */ *result = B_TRUE; if (!debug) break; ipp = (struct ip *)inpack; if (r_cnt != ntohs(ipp->ip_len)) { /* bogus IP header */ dhcpmsg(LOG_NOTICE, "Malformed ICMP message " "received from host %s: len %d != %d\n", ntoab, r_cnt, ntohs(ipp->ip_len)); break; } ip_hlen = ipp->ip_hl << 2; if (r_cnt < (int)(ip_hlen + ICMP_MINLEN)) { dhcpmsg(LOG_NOTICE, "ICMP message received " "from host %s is too small.\n", ntoab); break; } icp = (struct icmp *)((uint_t)inpack + ip_hlen); if (ipv4cksum((uint16_t *)icp, ntohs(ipp->ip_len) - ip_hlen) != 0) { dhcpmsg(LOG_NOTICE, "Bad checksum on incoming " "ICMP echo reply. (ping %s)\n", ntoab); } if (icp->icmp_type != ICMP_ECHOREPLY) { dhcpmsg(LOG_NOTICE, "Unexpected ICMP type %d from %s.\n", icp->icmp_type, ntoab); } if (icp->icmp_id != icmp_identifier) { dhcpmsg(LOG_NOTICE, "ICMP message id mismatch (from %s).\n", ntoab); } if (icp->icmp_seq != (sequence - 1)) { dhcpmsg(LOG_NOTICE, "ICMP sequence mismatch: " "%d != %d (ping %s)\n", icp->icmp_seq, sequence - 1, ntoab); } break; } } (void) close(s); return (error); }