0
点赞
收藏
分享

微信扫一扫

55 关于 icmp 的处理


前言

icmp 也是我们经常会碰到的协议 

比如 我们通常使用 ping 来测试 两台主机之间是否可以正常通信 

ping 是基于 icmp 协议的 

 

 

发送 icmp 数据包

测试用例如下, 封装了一个 icmp 报文, 并发送 

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<arpa/inet.h>
#include<sys/types.h>
#include<netinet/in.h>
#include<sys/socket.h>
#include<unistd.h>
#include<pthread.h>
#include<poll.h>
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<fcntl.h>
#include<sys/stat.h>
#include<signal.h>
#include<sys/sem.h>
#include<poll.h>
#include<pthread.h>
#include<sys/select.h>
#include<sys/un.h>
#define SA (struct sockaddr*)

#include<netinet/ip.h>
#include<netinet/ip_icmp.h>


unsigned short
csum (unsigned short *buf, int nwords)
{
  unsigned long sum;
  for (sum = 0; nwords > 0; nwords--)
    sum += *buf++;
  sum = (sum >> 16) + (sum & 0xffff);
  sum += (sum >> 16);
  return ~sum;
}

int
main (int argc, char *argv[])
{
  if (argc != 2)
    {
      printf ("need destination for tracert\n");
      exit (0);
    }
  int sfd = socket (AF_INET, SOCK_RAW, IPPROTO_ICMP);
  char buf[4096] = { 0 };
  struct ip *ip_hdr = (struct ip *) buf;
  int hop = 0;

  int one = 1;
  const int *val = &one;
  if (setsockopt (sfd, IPPROTO_IP, IP_HDRINCL, val, sizeof (one)) < 0)
    printf ("Cannot set HDRINCL!\n");

  struct sockaddr_in addr;
  addr.sin_port = htons (7);
  addr.sin_family = AF_INET;
  inet_pton (AF_INET, argv[1], &(addr.sin_addr));


  while (1)
    {
      ip_hdr->ip_hl = 5;
      ip_hdr->ip_v = 4;
      ip_hdr->ip_tos = 0;
      ip_hdr->ip_len = 20 + 8;
      ip_hdr->ip_id = 10000;
      ip_hdr->ip_off = 0;
      ip_hdr->ip_ttl = hop;
      ip_hdr->ip_p = IPPROTO_ICMP;
      inet_pton (AF_INET, "192.168.1.2", &(ip_hdr->ip_src));
      inet_pton (AF_INET, argv[1], &(ip_hdr->ip_dst));
      ip_hdr->ip_sum = csum ((unsigned short *) buf, 9);

      struct icmphdr *icmphd = (struct icmphdr *) (buf + 20);
      icmphd->type = ICMP_ECHO;
      icmphd->code = 0;
      icmphd->checksum = 0;
      icmphd->un.echo.id = 0;
      icmphd->un.echo.sequence = hop + 1;
      icmphd->checksum = csum ((unsigned short *) (buf + 20), 4);
      sendto (sfd, buf, 28, 0, SA & addr, sizeof addr);
      char buff[4096] = { 0 };
      struct sockaddr_in addr2;
      socklen_t len = sizeof (struct sockaddr_in);
      recvfrom (sfd, buff, 28, 0, SA & addr2, &len);
      struct icmphdr *icmphd2 = (struct icmphdr *) (buff + 20);
      if (icmphd2->type != 0)
    printf ("hop limit:%d Address:%s\n", hop, inet_ntoa (addr2.sin_addr));
      else
    {
      printf ("Reached destination:%s with hop limit:%d\n",
          inet_ntoa (addr2.sin_addr), hop);
      exit (0);
    }

      hop++;
    }

  return 0;
}

 

 

如何查看 skb 的数据 

skb 有一个 header, 表示的是数据块的头部 

skb 有一个 data, 表示的是 eth数据内容 的开始的地方, 通常来说对应于 ip包 头部

skb 有一个 tail, 表示的是当前 eth 包末尾的位置 

 

skb 有一个 mac_len 表示的是 eth头 的长度

skb 有一个 len 表示的是 eth数据内容 的长度

 

skb 有一个 mac_header 表示的是 header + mac_header 为 eth头 的开始的位置

skb 有一个 network_header 表示的是 header + network _header 为 ip头 的开始的位置

skb 有一个 transport_header 表示的是 header + transport _header 为 传输层头 的开始的位置

55 关于 icmp 的处理_icmp

 

内存中 skb 对应的 icmp 报文数据剖析 

(gdb) x /10gx 0xffff88007fa8f000
0xffff88007fa8f000:	0x000000000000f200	0x0008000000000000
0xffff88007fa8f010:	0x000027101c000045	0x0201a8c066270100
0xffff88007fa8f020:	0xfff600080201a8c0	0x0000000000010000
0xffff88007fa8f030:	0x0000000000200000	0x0000000600000001
0xffff88007fa8f040:	0x0000000000025bc0	0x0000000000225bc0 


# eth 
destMac : 000000000000
srcMac  : 000000000000
ethType : 0800

# ip
version+headerLen : 45
diffSerField : 00
totalLen : 001c
identification : 2710 
flags : 0000
timeToLive : 00 
protocol : 01 
header checksum : 2766 
srcIp : c0 a8 01 02
dstIp : c0 a8 01 02

# icmp 
type : 08
code : 00
checksum : f6ff
identifier be : 0000
sequence be : 0001

 

 

icmp 信息的发送 

参见 测试代码

 

 

icmp 信息的接受

首先是软中断, 然后 转给对应的设备 使用 net_rx_action 处理

55 关于 icmp 的处理_bc_02

 

 

到 ip 层的处理, protocol 为 1, 表示 icmp, 然后使用 icmp_protocol->handler 来处理当前请求 

55 关于 icmp 的处理_#include_03

 

然后是 icmp 协议层的处理, 请求类型为 ECHO, 使用 icmp_echo 进行处理 

55 关于 icmp 的处理_数据_04

 

 

封装 icmp 响应, 并返回给客户端 

55 关于 icmp 的处理_linux_05

55 关于 icmp 的处理_icmp_06

 

接下来就是 ip 层 以及 以太网层 的处理了

55 关于 icmp 的处理_数据_07

 

 

 

 

 

举报

相关推荐

0 条评论