Linux网络编程---ICMP协议分析及ping程序实现

一、IP协议

IP协议是TCP/IP协议族所依赖的传送机制,提供无连接不可靠的数据报服务。IP的无连接特性意味着每个IP报文都是独立寻径的,因此当一个源主机发送多个报文给同一目的主机时,这些报文可能出现错序,丢失或者部分报文产生错误等现象,因此为了保证数据传送的可靠性,必须在IP层之上通过TCP协议提供有序,带确认数据的传输服务。

1.IP协议格式

IP报文由报文头部和数据两部分构成,其中头部信息格式如下图所示,头部占20-60个字节,无选项option时,头部为20字节,最多可以携带40字节选项,报文最大长度为65535字节。


(1)版本(version)  4比特,定义了当前IP协议的版本,目前通常是数字4,即IPV4

(2)头部长度(ihl)   4比特,按4字节单位定义IP报文的头部总长度,因此未携带任何选项的IP报文头部长度为20字节,则ihl值为5(5*4=20),当选项长度达到最大值40字节时,ihl长度为15 (15*4=60)。

(3)服务类型(tos)  8比特,用于指示路由器将如何处理IP报文

(4)总长度(tot_len)16比特,报文头部加数据的总长度,IP报文携带的上层数据长度为:数据长度=总长度-头部长度=总长度-(ihl*4)。之所以需要总长度这个字段,是因为在某些情况下底层协议为了满足最小帧长的限制,会添加填充数据,例如以太协议要求每个数据帧最小必须为46字节,当来自上层的IP报文总长度小于46字节时,将添加填充数据以满足最小帧长,于是必须通过总长度这个字段来记录实际IP层报文的总长度,参考如图所示:


(5)报文标识(id)  16比特,用于标识多个IP分段所对应的原始IP分组的ID。

(6)分段标识(frag)3比特,用于声明一个IP报文是否是某个原始报文的分段,或者声明是否允许一个IP原始报文被分段。

(7)分段偏移(offset) 13比特,标识一个IP分段的数据在原始IP报文中的偏移值,注意该值,必须是8的整数倍。

(8)生存时间(ttl)   8比特, 一个IP报文在网上所允许的最大生存时间,该值实际为最大跳数,当源主机产生一个IP报文后,该字段将填写一个初始值,随后该报文每经过一个路由器则路由器将对该字段值进行减一操作,当该字段值变成0后,路由器将丢弃此报文。

(9)协议(protocol) 8比特,用于标识IP报文承载的上层数据的协议类型,例如可以是TCP,UDP,ICMP和IGMP等。

(10)头部校验和(check) 16比特,IP头部数据的检验和。

(11)源地址(saddr) 32比特,报文的源IP地址。

(12)目的地址(daddr)32比特,报文的目的IP地址。

(13)选项(option) 变长且最大不超过40字节。


2.IP协议头的c语言定义

struct iphdr
{
#if defined _LITTLE_ENDIAN_BITFIELD //小端机
    u8 hlen:4,
ver: 4;
#elif defined _BIG_ENFIAN_BITFELD  //大端机
    u8 ver:4,
hlen:4;
#endif
    
    u8 tos;
    u16 tot_len;
    u16 id;
    u16 frag_off;
    u8 ttl;
    u8 protocol;
    u16 check;
    u32 saddr;
    u32 daddr;
};

二、ICMP协议

(1)ICMP消息类型

ICMP消息分为两大类,错误报告消息和查询消息,这里仅介绍查询消息,每个查询消息类型均包括一对请求和应答消息。


(2)ICMP消息通用格式

ICMP消息包括8字节的头部和变长数据两个部分,其中所有消息类型头部的前4个字节均相同,头部其余4个字节随消息的不同而不同。如图所示:


ICMP消息头部的头4个字节分别是消息类型tye,消息代码code和校验和checksum,其中checksum字段包括头部和数据两部分,而并非仅头部,查询消息的数据部分data包含了用于查询所需要的额外数据。

(3)ICMP查询请求和应答消息格式

ICMP回应请求(echo-request)和应答消息(echo-reply)用于诊断两个系统(主机或路由器)之间是否能够进行通信,当其中一方发送回应请求消息给另一方时,接收到回应请求消息的主机或者路由器将以应答消息进行应答,常用的网络ping命令就是基于此消息类型的,如下图所示 其中type字段为8表示回应请求,0表示应答,code字段暂未是要你管,为0.


(4)ICMP消息格式的C语言定义

 

struct icmphdr
{
    u8 type;
    u8 code;
    u16 checksum;
    union
    {
        struct
        {
            u16 id;
            u16 sequence;
        }echo;
        
        u32 gateway;
        struct
        {
            u16 unused;
            u16 mtu;
        }frag; //pmtu发现
    }un;
    
    //u32  icmp_timestamp[2];//时间戳
    //ICMP数据占位符
    u8 data[0];
#define icmp_id un.echo.id
#define icmp_seq un.echo.sequence
};

ping程序实现:

#include<stdio.h>
#include<stdlib.h>
#include<sys/time.h>
#include<unistd.h>   
#include<string.h>
#include<sys/socket.h>
#include<sys/types.h>
#include<netdb.h>
#include<errno.h>
#include<arpa/inet.h>
#include<signal.h>
#include<netinet/in.h>

#ifndef _LITTLE_ENDIAN_BITFIELD
#define _LITTLE_ENDIAN_BITFIELD
#endif

#define IP_HSIZE sizeof(struct iphdr)   //定义IP_HSIZE为ip头部长度
#define IPVERSION  4   //定义IPVERSION为4,指出用ipv4



#define ICMP_ECHOREPLY 0 //Echo应答
#define ICMP_ECHO      8 //Echo请求

#define BUFSIZE 1500     //发送缓存最大值
#define DEFAULT_LEN 56   //ping 消息数据默认大小

//数据类型别名
typedef unsigned char u8;
typedef unsigned short u16;
typedef unsigned int u32;

//ICMP消息头部

struct icmphdr
{
    u8 type;
    u8 code;
    u16 checksum;
    union
    {
        struct
        {
            u16 id;
            u16 sequence;
        }echo;
        
        u32 gateway;
        struct
        {
            u16 unused;
            u16 mtu;
        }frag; //pmtu发现
    }un;
    u32  icmp_timestamp[2];//时间戳
    //ICMP数据占位符
    u8 data[0];
#define icmp_id un.echo.id
#define icmp_seq un.echo.sequence
};

#define ICMP_HSIZE sizeof(struct icmphdr)
struct iphdr
{
#if defined _LITTLE_ENDIAN_BITFIELD
    u8 hlen:4,
ver: 4;
#elif defined _BIG_ENFIAN_BITFELD
    u8 ver:4,
hlen:4;
#endif
    
    u8 tos;
    u16 tot_len;
    u16 id;
    u16 frag_off;
    u8 ttl;
    u8 protocol;
    u16 check;
    u32 saddr;
    u32 daddr;
};
char hello[]="hello this is  a ping test.";
char *hostname; //被ping的主机
int  datalen=DEFAULT_LEN;//ICMP消息携带的数据长度
char sendbuf[BUFSIZE];
char recvbuf[BUFSIZE];
int nsent;//发送的ICMP消息序号
int nrecv;
pid_t pid;//ping程序的进程pid
struct timeval recvtime; //收到ICMP应答的时间戳
int
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值