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 }