Linux下使用原始套接字实现ping 功能

hickwu 2011-02-18

//获得某个网卡的IP地址

char*   GetIpAddress(const char* interfaceName)

{

  register int fd, intrface;
  struct ifreq buf[MAXINTERFACES];   //#define MAXINTERFACES    12
  struct ifconf ifc;
  if ((fd = socket (AF_INET, SOCK_DGRAM, 0)) >= 0)
  {
    ifc.ifc_len = sizeof buf;
    ifc.ifc_buf = (caddr_t) buf;
    if (!ioctl (fd, SIOCGIFCONF, (char *) &ifc))
    {
      intrface = ifc.ifc_len / sizeof (struct ifreq);
      while (intrface-- > 0)
      {
        if (0 != strcmp(buf[intrface].ifr_name, interfaceName))
          continue;   

        //get the ip address of this net device
        if (!(ioctl (fd, SIOCGIFADDR, (char *) &buf[intrface])))
        {
          ::close(fd);
          return (inet_ntoa(((struct sockaddr_in*)(&buf[intrface].ifr_addr))->sin_addr));
        }
        else
        {
          char str[256];
          sprintf (str, "cpm: ioctl device %s", buf[intrface].ifr_name);
          perror (str);
        }
      }
    }

    ::close(fd);
  }
return "";

}

bool   IsValidIpAddress(const char* ipAddress)

{

   struct sockaddr_in sin;

  sin.sin_family = AF_INET;
  memset(&sin, 0, sizeof(sin));
  if (inet_pton(AF_INET, ipAddress, &sin.sin_addr) <= 0)
  {
    return false;
  }
  else
  {
    char strConvertedIP[MAX_IP_LENGTH];    //#define MAX_IP_LENGTH     30
    if (NULL != inet_ntop(AF_INET, &sin.sin_addr, strConvertedIP, MAX_IP_LENGTH) )
    {
      
      if(0 != strcmp(strConvertedIP, "0.0.0.0"))
      {
        return true;
      }
    }
    return false;
  }

}

 bool    send_echo_req(int sock_fd,struct sockaddr_in *dstaddr)

{

   char buf[100];
  size_t len = sizeof(struct icmp);
  struct icmp *icmp;
  socklen_t dstlen = sizeof(struct sockaddr_in);

  bzero(buf, sizeof(buf));
  icmp = (struct icmp *)buf;
  icmp->icmp_type = ICMP_ECHO;
  icmp->icmp_code = 0;
  icmp->icmp_id = getpid();
  icmp->icmp_seq = 1;
  icmp->icmp_cksum = in_cksum((uint16_t *) icmp, sizeof(struct icmp));

  if (sendto(sockfd, buf, len, 0, (struct sockaddr*)dstaddr, dstlen) == -1)
  {
    err_sys("sendto");
    return false;
  }
  return true;
}

bool     recv_echo_reply(int sock_fd)

{

   char buf[100];
  ssize_t n;
  struct ip *ip;
  struct icmp *icmp;
 
  fd_set rfds;
  struct timeval tv;
  int retval;

  FD_ZERO(&rfds);
  FD_SET(sockfd, &rfds);

  tv.tv_sec = 5;
  tv.tv_usec = 0;

  retval = select(sockfd+1, &rfds, NULL, NULL, &tv);

  if (retval == -1)
  {
    perror("select()");
    return false;
  }
  else if (retval)
  {
    /* FD_ISSET(0, &rfds) will be true. */
    if ((n = read(sockfd, buf, sizeof(buf))) == -1)
    {
      err_sys("read");
      return false;
    }
    ip = (struct ip *)buf;
    if (ip->ip_p != IPPROTO_ICMP) {
      fprintf(stderr, "protocol error.\r\n");
      return false;
    }
    icmp = (struct icmp *)(buf + sizeof(struct ip));
    if (icmp->icmp_type == ICMP_ECHOREPLY) {
      if (icmp->icmp_id != getpid()) {
        fprintf(stderr, "not this process.\r\n");
        return false;
      } else {
        //printf("destination host is alive.\r\n");
        return true;
      }
    }
  }
  //printf("ping timeout.\n");
  return false;

}

uint16_t   in_cksum(uint16_t*  addr, int len)

{

   int nleft = len;
  uint32_t sum = 0;
  uint16_t *w = addr;
  uint16_t answer = 0;

  while (nleft > 1) {
    sum += *w++;
    nleft -= 2;
  }

  if (nleft == 1) {
    *(unsigned char *)(&answer) = *(unsigned char *)w ;
    sum += answer;
  }

  sum = (sum >> 16) + (sum & 0xffff);
  sum += (sum >> 16);
  answer = ~sum;
  return(answer);

}

bool    Ping (char* dest)

{

   int sockfd;
  struct sockaddr_in dstaddr;
  bool ret;
 
  char*  ipAddr;
  ipAddr = GetIPAddress(m_wirelessNetworkDevice);
  if(!IsValidIPAddress(ipAddr.toUtf8().constData()))
    return false;
  if ((sockfd = socket(PF_INET, SOCK_RAW, IPPROTO_ICMP)) == -1)   //ICMP协议,原始套接字类型
  {
    err_sys("socket");
    return false;
  }
  bzero(&dstaddr, sizeof(dstaddr));
  dstaddr.sin_family = AF_INET;
  dstaddr.sin_port = htons(0);
  //printf("dest is %s\n",dest);
  if (inet_pton(AF_INET, dest, &dstaddr.sin_addr) <= 0)
  {
    err_sys("inet_pton");
    ::close(sockfd);
    return false;
  }
  ret = send_echo_req(sockfd, &dstaddr);
  if (!ret)
  {
    ::close(sockfd);
    return false;
  }
  ret = recv_echo_reply(sockfd);
  if (ret)
  {
    ::close(sockfd);
    return true;
  }
  else
  {
    ::close(sockfd);
    return false;
  }

}

 以上是Linux下的代码。

相关推荐