c++ - 不要使用 C++ 在 Linux 中为 IPv6 数据包分段标志

标签 c++ ipv6 icmp

我无法为 IPv6/ICMPv6 数据包设置“不分段”标志。我正在执行 PMTUD,我想强制路由器丢弃大于 MTU 的数据包。将 setsockopt 与 IPV6_MTU_DISCOVER 一起使用无效。

int on = IPV6_PMTUDISC_DO; // tried also IPV6_PMTUDISC_PROBE
setsockopt(socket, IPPROTO_IPV6, IPV6_MTU_DISCOVER, &on, sizeof(on));

结果: wireshark

我也不能按照 Unix-Linux Addison-Wesley - Stevens2003 - Unix Network Programming 中所述将 setosckopt 与 IPV6_DONTFRAG 一起使用,因为我有 netinet/in6 .h header 和 IPV6_DONTFRAGlinux/in6.h 中定义。在我的源代码中包含 linux/in6.h 会导致这些重定义错误。

In file included from mypmtud.cc:30:0: /usr/include/linux/in6.h:30:8: error: redefinition of ‘struct in6_addr’ In file included from /usr/include/netdb.h:28:0,
                 from mypmtud.cc:23: /usr/include/netinet/in.h:198:8: error: previous definition of ‘struct in6_addr’ In file included from mypmtud.cc:30:0: /usr/include/linux/in6.h:46:8: error: redefinition of ‘struct sockaddr_in6’ In file included from /usr/include/netdb.h:28:0,
                 from mypmtud.cc:23: /usr/include/netinet/in.h:239:8: error: previous definition of ‘struct sockaddr_in6’ In file included from mypmtud.cc:30:0: /usr/include/linux/in6.h:54:8: error: redefinition of ‘struct ipv6_mreq’ In file included from /usr/include/netdb.h:28:0,
                 from mypmtud.cc:23: /usr/include/netinet/in.h:275:8: error: previous definition of ‘struct ipv6_mreq’ make: *** [mypmtud] Error 1

环境:VirtualBox 4.26 上的 Ubuntu 12.10 和用于虚拟网络的 GNS3。虚拟Cisco C3660路由器只有基本配置:ip、ipv6地址、no shut和set mtu。

编辑: 我需要 IPv6 堆栈/操作系统内核丢弃大于链路 MTU 的数据包或发出“此数据包需要分段”的信号。我怎样才能实现这种行为?

我尝试了 setsockoptIPV6_DONTFRAG (在我的代码中定义了它 #define IPV6_DONTFRAG 62 ), setsockoptIPV6_MTU_DISCOVERint on = IPV6_PMTUDISC_DOsetsockopt 以及 IPV6_RECVPATHMTU

但我没有得到 PACKET TOO BIG 回复或 辅助数据 cmsg_level == IPPROTO_IPV6cmsg_type == IPV6_PATHMTU .

我的部分代码:

/** sending ICMP packet*/
 if (((length = sendto(mysocket, packet, lengthBuff, 0, result->ai_addr, result->ai_addrlen)) < 0) && (errno == EMSGSIZE)){

     // works for IPv4, doesn't work with IPv6
     cout << "changing maxBuff and lengthBuff size" << endl;

        maxBuff = lengthBuff;
        lengthBuff = (minBuff + maxBuff) / 2;

        if (packet) {
            delete(packet);
            packet = NULL;
        }


    } else if (length < 0){

     cerr << "Error: sending data." << endl;

            freeaddrinfo(result);
            close(mysocket);

            if (packet) {
                delete(packet);
                packet = NULL;
            }

            exit(1);
    } else if(((recvmsg(mysocket, &msg, 0)) != -1) && (errno != EINTR)) {

        // reading ancillary dada as described in  *Unix-Linux Addison-Wesley - Stevens2003 - Unix Network Programming, page 736*
        cmsgh = CMSG_FIRSTHDR(&msg);    

        if(cmsgh != NULL) {
            cout << "getting msg " << endl;
            cout << "msg len " << msg.msg_controllen << endl;


            if(cmsgh->cmsg_level == IPPROTO_ICMPV6 && cmsgh->cmsg_type == IPV6_PATHMTU)
            {
                cout << "CMSGHEADER - GOOD" << endl;
                //mtustruct = CMSG_DATA(&msg); 

            maxBuff = lengthBuff;
            lengthBuff = (minBuff + maxBuff) / 2;

            if (packet) {
                delete(packet);
                packet = NULL;
            }

            }
            else{

                cout << "different ancillary data. " << endl;
                cout << " level " << cmsgh->cmsg_level << " type " << cmsgh->cmsg_type << endl;
            }
        }

    } else {

        cout << "no ERROR with sendto and no RESCVMSG" << endl;
    } 

    /** receiving ICMP data */
    tv.tv_sec = 3;
    tv.tv_usec = 0;

    int retval; // select

        FD_ZERO(&mySet);
        FD_SET(mysocket, &mySet);

        retval = select(mysocket + 1, &mySet, NULL, NULL, &tv);

        if (retval == -1) {
            cerr << "select failed" << endl;
            //break;
            exit(1);
        } else if (retval) {
            if ((length = recvfrom(mysocket, buffer, MAX, 0, result->ai_addr, &(result->ai_addrlen))) == -1) {
                cerr << "Error: receiving data." << endl;
            } else {
                icmpRec = (struct icmp6_hdr*) buffer;

                if((icmpRec->icmp6_type == ICMP6_PACKET_TOO_BIG)) {


                    cout << "next hop MTU: " << ntohl(icmpRec->icmp6_mtu) << endl;
                    maxBuff = ntohl(icmpRec->icmp6_mtu);                       

                } else if ((icmpRec->icmp6_type == ICMP6_ECHO_REPLY) && (ntohs(icmpRec->icmp6_id) == pid) && (ntohs(icmpRec->icmp6_seq) == (seq - 1))) {
                    cout << "code " << ntohs(icmpRec->icmp6_code) << endl;
                    cout << "ICMP ECHO REPLY" << endl;
                    minBuff = lengthBuff;

                }
            }
        }

编辑 2: 我意识到,setsockopt 和定义的 IPV6_DONTFRAG 对我不起作用,但是 setsockoptIPV6_MTU_DISCOVER 正在为自己工作界面。 eth1 接口(interface) MTU 是 1500(默认值),如果 sendto 想要发送更大的数据包,errno 设置为 EMSGSIZE。一段时间后,我收到 PACKET TOO BIG 消息,因为这些消息未从 自己的内核/操作系统 发送消息。

我真正的问题是,我没有从运行在 VirtualBox 4.2.6 上的虚拟路由器 (Cisco c3660) GNS3.

最佳答案

and I want to force the router to drop packets bigger then the MTU

在 IPv6 中,大于 MTU 的数据包将始终被丢弃。与 IPv4 不同,IPv6 路由器不会对数据包进行分段。相反,源应该执行 PMTU 和:

  • 让传输层协议(protocol)产生足够大小的数据报
  • 本地对数据包进行分段并附加一个分段扩展

Linux 确实完全支持 IPV6_DONTFRAG(我认为它是在 2.6.35 中添加的)尽管它只影响本地行为。

关于c++ - 不要使用 C++ 在 Linux 中为 IPv6 数据包分段标志,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14185610/

相关文章:

ruby-on-rails - 如何检查字符串中的值是否为 IP 地址

android - 无法与同一网络上的 Android 设备 ping/通信

c - ICMPv6 套接字创建失败

java - 如何在运行时设置 java.net.preferIPv4Stack=true?

ios - 如何为 iOS 应用程序测试设置仅 IPv6 网络?

c# - XNA 与 C++ 引擎

c++ - 非托管 C++ tlh 文件未更新?

c++ - InetPton() 将任何 IP 转换为 1.0.0.0

c++ - 为 Boost Fusion 调整一个空结构

c++ - SFML C++无法加载字体