Linux 下C/C++实现发送ICMP和ICMPv6(报文分析)
我们日常使用最多的ping,就是响应请求(Type=8)和应答(Type=0),一台主机向一个节点发送一个Type=8的ICMP报文,如果途中没有异常(例如被路由器丢弃、目标不回应ICMP或传输失败),则目标返回Type=0的ICMP报文,说明这台主机存在,更详细的tracert通过计算 ICMP报文通过的节点来确定主机与目标之间的网络距离。一种称为Internet控制消息协议(ICMP)的特殊协
当终端系统无法到达目的地的IP数据包时,为了方便获取诊断信息。一种称为Internet控制消息协议(ICMP)的特殊协议与IP结合使用,以提供与IP协议层配置和IP数据包处理相关的诊断和控制信息。主要的原因是IP协议没有内置机制,在源设备发送的数据包无法到达目标时通知源设备。对于错误报告和控制,它取决于ICMP协议。
ICMP和ICMPv6的报头格式
下图显示了ICMPv4和ICMPv6消息的格式。前4个字节对所有消息都具有相同的格式,但其余的字节因消息而异。
类型字段用于确定特定的报文,ICMPv4和ICMPv6的该字段值并不相同;代码字段进一步肯定报文的含义,ICMPv4和ICMPv6的该字段值并不相同;校验和字段用于确定报文信息的正确性。
- Wireshark抓包获取ICMP(以Echo (ping)request为例):
我们日常使用最多的ping,就是响应请求(Type=8)和应答(Type=0),一台主机向一个节点发送一个Type=8的ICMP报文,如果途中没有异常(例如被路由器丢弃、目标不回应ICMP或传输失败),则目标返回Type=0的ICMP报文,说明这台主机存在,更详细的tracert通过计算 ICMP报文通过的节点来确定主机与目标之间的网络距离。
对于ICMPv4,信息消息包括:
回声请求(类型8)
回音回复(类型0)
路由器播发(类型9)
路由器请求(类型10)
路由器广告和路由器请求统称为路由器发现。最常见的错误消息类型包括:
目标无法访问(类型3)
重定向(类型5)
超过时间(类型11)
参数问题(类型12)
- Wireshark抓包获取ICMPv6(以Echo (ping)request为例):
类型:标识ICMPv6报文类型,它的值根据报文的内容来确定。
代码:用于确定ICMPv6进一步的信息,对同一类型的报文进行了更详细的分类。
校验和:用于检测ICMPv6的报文是否正确传送。
报文体:用于返回出错的参数和记录出错报文的片段,帮助源结点判断错误的原因。或是其它参数。
类型字段值为128 ,表示该ICMPv6信息报文中的回送请求报文。代码字段在发送端置为0校验和字段在计算之前被置0.标识字段与序列号字段用于在回送请求报文与回送应答报文之间建立对应关系。数据字段是诊断的内容,为8b的整数倍。
在ICMPv6中,与ICMPv4中一样,消息被分组为信息类和错误类。
然而,在ICMPv6中,所有错误消息的类型字段的高位都有一个0。因此,ICMPv6类型0到127都是错误,类型128到255都是信息性的。许多信息性消息是请求/回复对。
Linux发送ECHO request消息C/C++代码实现
...
int main(int argc, char *argv[])
{
...
for (int i=1; i<argc; ++i)
{
const char *const arg = argv[i];
union
{
struct sockaddr_in sin;
struct sockaddr_in6 sin6;
} u;
bzero(&u, sizeof(u));
if (0 == strcmp("--help", arg))
{
print_usage();
return 0;
}
else if (0 == strcmp("--version", arg))
{
print_version();
return 0;
}
else if ((0 == strcmp("--dry-run", arg)) || (0 == strcmp("-n", arg)))
{
dry_run = true;
continue;
}
else if (0 == strcmp("--loop", arg))
{
do_loop = true;
continue;
}
else if (0 == strcmp("-qq", arg))
{
switch (verbosity)
{
case VERB_NORMAL:
verbosity = VERB_MUTE;
break;
default:
error_exit("Illegal use of -qq");
}
continue;
}
else if ((0 == strcmp("--quiet", arg)) || (0 == strcmp("-q", arg)))
{
switch (verbosity)
{
case VERB_NORMAL:
verbosity = VERB_QUIET;
break;
case VERB_QUIET:
verbosity = VERB_MUTE;
break;
default:
error_exit("Illegal use of --quiet/-q");
}
continue;
}
else if (0 == strcmp("-vv", arg))
{
switch (verbosity)
{
case VERB_NORMAL:
verbosity = VERB_VERY;
break;
default:
error_exit("Illegal use of -vv");
}
continue;
}
else if ((0 == strcmp("--verbose", arg)) || (0 == strcmp("-v", arg)))
{
switch (verbosity)
{
case VERB_NORMAL:
verbosity = VERB_VERBOSE;
break;
case VERB_VERBOSE:
verbosity = VERB_VERY;
break;
default:
error_exit("Illegal use of --verbose/-v");
}
continue;
}
else if (1 == inet_pton(AF_INET6, arg, &u.sin6.sin6_addr))
{
enlarge_array(arg);
u.sin6.sin6_family = AF_INET6;
memcpy(&tasks[task_cnt].sas, &u.sin6, sizeof(u.sin6));
}
else if (1 == inet_pton(AF_INET, arg, &u.sin.sin_addr))
{
enlarge_array(arg);
u.sin.sin_family = AF_INET;
memcpy(&tasks[task_cnt].sas, &u.sin, sizeof(u.sin));
}
else
{
error_exitf("Cannot parse address: %s", arg);
}
tasks[task_cnt].delay.tv_sec = 0;
tasks[task_cnt].delay.tv_nsec = 500000000;
++task_cnt;
}
...
return 0;
}
...
发送回显请求将一个ICMP(IPv4)或ICMPv6(IPv6)回显请求分别发送到一个地址列表,而无需等待回显回复数据包。每0.5秒发送一个数据包。或者,在无限循环中重复发送数据包。
运行结果:
tcpdump抓取ICMP ECHO request消息:
使用
./send_echo_request -q --loop 39.156.66.10
使用-q选项可以抑制信息回显在终端。
tcpdump抓取ICMP ECHO request消息:
发送ICMPv6 echo request消息:
tcpdump抓包
tcpdump -i ens33 “icmp6 && ip6[40] == 128”
总结
在IPv4中,Internet控制报文协议ICMP向源节点报告关于向目的地传输IP数据包过程中的错误和信息。在IPv6中,ICMPv6除了提供ICMPv4常用的功能之外,还是其它一些功能的基础,如邻接点发现、无状态地址配置(包括重复地址检测)、PMTU发现等。最后写个测试例子来发送回显请求,并且进行抓包分析。有助于理解ICMP和ICMPv6
更多推荐
所有评论(0)