赞
踩
有关ICMP的内容在此不做阐述,仅说下ICMP报文何时会发送(ping除外),ICMP报文中的数据又是什么,如何处理接收到的ICMP报文
当发送一段udp报文到一个不可达的机器,或者ttl耗尽,或者MTU大于在某处路由的mtu,此时均会收到ICMP报文。上述所说均是常见例子,分别对应3,3;11,0;3,4
携带的正是你发送的UDP数据,会将其原原本本的发送回来,其中会包含IP层的数据
如下是通过wireshark捕获的3,3报文。此时由于客户端的突然关闭,但是路由链路还存在,故主机回收到此udp,然而程序退出,故回复了ICMP报文
下面分别是udp报文和ICMP报文
// udp数据
1f 40 d1 27 00 1c 2d 20
21 00 00 29 78 7e a0 a2 74 09 b9 7a 00 10 00 00 98 6a 48 23
// ICMP报文
03 03 96 ec 00 00 00 00 // ICMP头部,以下是数据部分
45 68 00 30 7f 8b 40 00 35 11 2c 08 31 ** ** dc c0 a8 14 55 // IP头部 **是屏蔽了公网IP
1f 40 d1 27 00 1c 2d 20 // udp头部
21 00 00 29 78 7e a0 a2 74 09 b9 7a 00 10 00 00 98 6a 48 23 // udp数据
1、设置套接字
int on = 1;
setsockopt(fd, SOL_IP, IP_RECVERR, &on, sizeof(on); // 仅限IPv4
2、通过poll/epoll返回 POLLERR/EPOLLERR后读取消息
unsigned char vec_buf[4096], ancillary_buf[4096]; struct iovec iov = {vec_buf, sizeof(vec_buf)}; struct sockaddr_in remote; struct msghdr msg; ssize_t len; struct cmsghdr *cmsg; struct sock_extended_err *e; struct sockaddr *icmp_addr; struct sockaddr_in *icmp_sin; memset(&msg, 0, sizeof(msg)); msg.msg_name = &remote; msg.msg_namelen = sizeof(remote); msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_flags = 0; msg.msg_control = ancillary_buf; msg.msg_controllen = sizeof(ancillary_buf); EINTR_LOOP(len, recvmsg(udpfd, &msg, MSG_ERRQUEUE | MSG_DONTWAIT)); if (len < 0) { if (errno != EAGAIN || errno != EWOULDBLOCK) { LOGE("recvmsg error. [%d,%s]", errno, strerror(errno)); } break; } // 收到几个ICMP就循环几次。可通过man 3 cmsg查看详情 for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) { if (cmsg->cmsg_type != IP_RECVERR) { LOGD("Unhandled errqueue type: %d\n", cmsg->cmsg_type); continue; } if (cmsg->cmsg_level != SOL_IP) { LOGD("Unhandled errqueue level: %d\n", cmsg->cmsg_level); continue; } LOGD("errqueue: IP_RECVERR, SOL_IP, len %zd\n", cmsg->cmsg_len); if (remote.sin_family != AF_INET) { LOGD("Address family is %d, not AF_INET. Ignoring\n", remote.sin_family); continue; } LOGD("Remote host: %s:%d\n", inet_ntoa(remote.sin_addr), ntohs(remote.sin_port)); e = (struct sock_extended_err *)CMSG_DATA(cmsg); if (!e) { LOGD("errqueue: sock_extended_err is NULL?\n"); continue; } if (e->ee_origin != SO_EE_ORIGIN_ICMP) { LOGD("errqueue: Unexpected origin: %d\n", e->ee_origin); continue; } LOGD(" ee_errno: %d\n", e->ee_errno); LOGD(" ee_origin: %d\n", e->ee_origin); LOGD(" ee_type: %d\n", e->ee_type); LOGD(" ee_code: %d\n", e->ee_code); LOGD(" ee_info: %d\n", e->ee_info); // discovered MTU for EMSGSIZE errors LOGD(" ee_data: %d\n", e->ee_data); // "Node that caused the error" // "Node that generated the error" icmp_addr = (struct sockaddr *)SO_EE_OFFENDER(e); icmp_sin = (struct sockaddr_in *)icmp_addr; if (icmp_addr->sa_family != AF_INET) { LOGD("ICMP's address family is %d, not AF_INET?\n", icmp_addr->sa_family); continue; } if (icmp_sin->sin_port != 0) { LOGD("ICMP's 'port' is not 0?\n"); continue; } LOGD("msg_flags: 0x%x", msg.msg_flags); // if (msg.msg_flags & MSG_TRUNC) // fprintf(stderr, " MSG_TRUNC"); // if (msg.msg_flags & MSG_CTRUNC) // fprintf(stderr, " MSG_CTRUNC"); // if (msg.msg_flags & MSG_EOR) // fprintf(stderr, " MSG_EOR"); // if (msg.msg_flags & MSG_OOB) // fprintf(stderr, " MSG_OOB"); // if (msg.msg_flags & MSG_ERRQUEUE) // fprintf(stderr, " MSG_ERRQUEUE"); // fprintf(stderr, "\n"); if (e->ee_type == 3 && e->ee_code == 4) { // utp猜测的MTU过大 LOGD("ICMP: type 3, code 4: Fragmentation error, discovered MTU %d\n", e->ee_info); utp_process_icmp_fragmentation(m_utpContex, vec_buf, len, (struct sockaddr *)&remote, sizeof(remote), e->ee_info); } else { LOGD("ICMP: type %d, code %d\n", e->ee_type, e->ee_code); utp_process_icmp_error(m_utpContex, vec_buf, len, (struct sockaddr *)&remote, sizeof(remote)); } }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。