• ICMP报文


    有关ICMP的内容在此不做阐述,仅说下ICMP报文何时会发送(ping除外),ICMP报文中的数据又是什么,如何处理接收到的ICMP报文

    何时产生ICMP

    当发送一段udp报文到一个不可达的机器,或者ttl耗尽,或者MTU大于在某处路由的mtu,此时均会收到ICMP报文。上述所说均是常见例子,分别对应3,3;11,0;3,4

    ICMP数据

    携带的正是你发送的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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    ICMP报文处理

    1、设置套接字

    int on = 1;
    setsockopt(fd, SOL_IP, IP_RECVERR, &on, sizeof(on); // 仅限IPv4
    
    • 1
    • 2

    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));
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101

    在这里插入图片描述
    在这里插入图片描述
    详看ICMP报文协议可至此

  • 相关阅读:
    安卓的分区一点有用知识:super、lpunpack、lpdump
    Python 学习 Day41
    HSRP和VRRP
    unity 获取3D物体的方向数据
    记一次进程阻塞诊断
    Spark 3.0 - 9.Ml 朴素贝叶斯中文分类分析与实战
    基于双向 GRU 的文本分类 Python 算法实战
    服务容错框架Sentinel入门
    《向量数据库》——都有哪些向量数据库,都有什么特点?
    2024年山东省职业院校技能大赛中职组 “网络安全”赛项竞赛试题-A卷
  • 原文地址:https://blog.csdn.net/DefiniteGoal/article/details/127651417