代码之家  ›  专栏  ›  技术社区  ›  es483

Linux套接字:为什么以太网MTU即使在通过802.11传输时也是一个限制?

  •  0
  • es483  · 技术社区  · 5 年前

    在使用原始和 SOCK_DRAM UDP套接字,我注意到以太网最大传输单元总是表示我可以发送的数据的限制,而不会导致碎片,即使套接字严格绑定到无线接口(使用 bind()

    我知道802.11MTU是2346B(正确吗?),比1500B大。但是,如果我尝试传输超过以太网MTU大小(1500 B)的数据,则在使用UDP套接字时会出现碎片和“消息太大”错误( errno 90 , EMSGSIZE )使用时 sendto() 在原始套接字上。

    这是一个从原始代码中提取的示例代码,您可以使用它进行编译 gcc 并用于重现我之前描述的问题。

    当使用原始套接字时,超过1500 B的消息被明确拒绝,设置 errno

    代码如下:

    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <linux/wireless.h>
    #include <sys/socket.h>
    #include <linux/if_packet.h>
    #include <net/ethernet.h>
    #include <arpa/inet.h>
    #include <string.h>
    #include <sys/ioctl.h>
    
    #define DEVNAME "wlp2s0"
    #define DESTINATIONMAC_INITIALIZER {0x9C,0xD2,0x1E,0x20,0x91,0xE5}
    
    #define SIZEOK 1500
    #define SIZEWRONG 1501
    
    int main (int argc, char **argv) {
        // wlanLookup() variables, for interface name, source MAC address and return value
        char devname[]=DEVNAME;
        int ifindex;
    
        int descriptor; // Socket descriptor
        struct sockaddr_ll addrll; // sockaddr_ll address structure
    
        struct ifreq wifireq;
    
        struct ether_header etherHeader;
    
        unsigned char bufferok[SIZEOK];
        unsigned char bufferwrong[SIZEWRONG];
    
        unsigned char *packetok=NULL;
        unsigned char *packetwrong=NULL;
    
        // Source and destination MAC addresses
        unsigned char macsrc[6];
        unsigned char macdst[6]=DESTINATIONMAC_INITIALIZER;
    
        // Size variables
        int sentbytes;
        size_t sizeok_final=sizeof(struct ether_header)+SIZEOK; // Size of the ok buffer + size of struct ether_header
        size_t sizewrong_final=sizeof(struct ether_header)+SIZEWRONG; // Size of the ok buffer + size of struct ether_header
    
        if(SIZEWRONG<SIZEOK) {
            fprintf(stderr,"Error: in this sample code, SIZEWRONG (%d) must be >= than SIZEOK (%d)\n",SIZEWRONG,SIZEOK);
            exit(EXIT_FAILURE);
        }
    
        descriptor=socket(AF_PACKET,SOCK_RAW,htons(ETH_P_ALL));
    
        if(descriptor==-1) {
            perror("socket() error");
            exit(EXIT_FAILURE);
        }
    
        // Get interface index of the interface
        strncpy(wifireq.ifr_name,devname,IFNAMSIZ);
        if(ioctl(descriptor,SIOCGIFINDEX,&wifireq)!=-1) {
            ifindex=wifireq.ifr_ifindex;
        } else {
            perror("ioctl() error");
            close(descriptor);
            exit(EXIT_FAILURE);
        }
    
        fprintf(stdout,"Using interface: %s (index: %x)\n",DEVNAME,ifindex);
    
        // Prepare sockaddr_ll structure
        memset(&addrll,0,sizeof(addrll));
        addrll.sll_ifindex=ifindex;
        addrll.sll_family=AF_PACKET;
        addrll.sll_protocol=htons(ETH_P_ALL);
    
        // Bind to the wireless interface
        if(bind(descriptor,(struct sockaddr *) &addrll,sizeof(addrll))<0) {
            perror("Cannot bind to interface: bind() error");
            close(descriptor);
            exit(EXIT_FAILURE);
        }
    
        fprintf(stdout,"Bound to interface: %s (index: %x)\n",DEVNAME,ifindex);
    
        // Populate both buffers with some data
        for(int i=0;i<SIZEWRONG;i++) {
            if(i<SIZEOK) {
                bufferok[i]=(unsigned char) (i & 15); // Fill each byte with a cyclic sequence 0x00, 0x01, 0x02, ... 0x0F, 0x00, 0x01, ...
            }
            bufferwrong[i]=(unsigned char) (i & 15);
        }
    
        // Get source MAC address
        strncpy(wifireq.ifr_name,devname,IFNAMSIZ); 
        if(ioctl(descriptor,SIOCGIFHWADDR,&wifireq)!=-1) {
            memcpy(macsrc,wifireq.ifr_hwaddr.sa_data,6);
        } else {
            perror("Cannot get source MAC address: ioctl() error");
            close(descriptor);
        }
        fprintf(stdout,"Source MAC address: %02x:%02x:%02x:%02x:%02x:%02x\n",macsrc[0],macsrc[1],macsrc[2],macsrc[3],macsrc[4],macsrc[5]);
        fprintf(stdout,"Destination MAC address: %02x:%02x:%02x:%02x:%02x:%02x\n",macdst[0],macdst[1],macdst[2],macdst[3],macdst[4],macdst[5]);
    
        // Fill struct ether_header
        memcpy(etherHeader.ether_dhost,macdst,ETHER_ADDR_LEN);
        memcpy(etherHeader.ether_shost,macsrc,ETHER_ADDR_LEN);
        // Using local experimental ethertype for the sake of this sample code, but the effect is the same for any other EtherType
        etherHeader.ether_type=htons(0x88B5);
    
        packetok=malloc(sizeok_final*sizeof(unsigned char));
        if(!packetok) {
            perror("malloc() error");
            close(descriptor);
            exit(EXIT_FAILURE);
        }
        packetwrong=malloc(sizewrong_final*sizeof(unsigned char));
    
        if(!packetwrong) {
            perror("malloc() error");
            free(packetok);
            close(descriptor);
            exit(EXIT_FAILURE);
        }
    
        // Generate the complete packet buffers
        // Packet OK
        memcpy(packetok,&etherHeader,sizeof(struct ether_header));
        memcpy(packetok+sizeof(struct ether_header),bufferok,sizeok_final);
    
        // Packet WRONG
        memcpy(packetwrong,&etherHeader,sizeof(struct ether_header));
        memcpy(packetwrong+sizeof(struct ether_header),bufferwrong,sizewrong_final);
    
        sentbytes=sendto(descriptor,packetok,sizeok_final,0,(struct sockaddr *)&addrll,sizeof(struct sockaddr_ll));
    
        perror("Packet OK errors (if any)");
        fprintf(stdout,"Packet OK: sent %d bytes.\n",sentbytes);
    
        sentbytes=sendto(descriptor,packetwrong,sizewrong_final,0,(struct sockaddr *)&addrll,sizeof(struct sockaddr_ll));
    
        perror("Packet WRONG errors (if any)");
        fprintf(stdout,"Packet WRONG: sent %d bytes.\n",sentbytes);
    
        close(descriptor);
    
        return 0;
    }
    

    在编译之前,您应该设置 DEVNAME 示例程序应绑定到的接口名称,以及 DESTINATIONMAC_INITIALIZER

    我实际上得到了这个输出(在用 sudo ),显示超过1500 B的数据包如何被拒绝:

    Using interface: wlp2s0 (index: 3)
    Bound to interface: wlp2s0 (index: 3)
    Source MAC address: 00:16:ea:4a:bd:7e
    Destination MAC address: 9c:d2:1e:20:91:e5
    Packet OK errors (if any): Success
    Packet OK: sent 1514 bytes.
    Packet WRONG errors (if any): Message too long
    Packet WRONG: sent -1 bytes.
    

    当启动示例程序三次时,“ok”数据包被目标设备正确接收,而所有“错误”数据包则没有: Wireshark capture screenshot

    0 回复  |  直到 5 年前
        1
  •  0
  •   es483    5 年前

    这个问题最终由位于的用户解决了 LinuxQuestions

    事实上,我混淆了 协议MTU 其中一个 接口MTU .

    ip link set <dev> mtu <mtu> . 如果硬件实际支持指定的MTU,则此命令可能成功;如果硬件不支持,则此命令可能失败。

    通过增加无线接口的MTU设置,我成功地通过Wi-Fi传输了更大的帧,直到最大值达到2304 B。

    相反,如果我尝试设置更大的接口MTU,则会出现以下错误,这在某种程度上是意料之中的:

    $ sudo ip link set wlp1s0 mtu 2305
    Error: mtu greater than device maximum.
    

    最后,可以使用以下方法检查每个活动接口当前设置的接口MTU,例如:

    ifconfig | grep -i MTU
    
    推荐文章