1
votes

While writing a program using RAW and SOCK_DRAM UDP sockets, I noticed that the Ethernet Maximum Transmission Unit is always representing a limit to the data I can send without incurring into fragmentation, even if the socket is strictly bound to a wireless interface (using bind()) and the packets are always transmitted on the air using Wi-Fi (802.11), without any wired network segment in between.

I knew the 802.11 MTU to be 2346 B (is it correct?), which is larger than 1500 B. But if I try to transmit more than the Ethernet MTU size (1500 B), I get fragmentation when using UDP sockets and a "message too big" error (errno 90, EMSGSIZE) when using sendto() on RAW sockets.

Is this due to the fact that, from the point of view of the user applications, 802.11 packets are seen as 802.3 packets which are then converted and managed inside the kernel and hardware devices? Why is this limit applying even if I could transmit larger frames with "Wi-Fi"?

Edit: sample code to reproduce the problem for RAW sockets

Here is a sample code, extracted from the original one, that you can compile with gccand use to reproduce the problem I described before.

When using RAW sockets a message over 1500 B is explicitely rejected, setting errno, even when the socket is bound to a wireless interface.

Here is the code:

#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;
}

Before compiling, you should set DEVNAME to the name of the interface the sample program should bind to and DESTINATIONMAC_INITIALIZER to the destination device you want to try to send packets to.

I actually got this output (after running the program with sudo), showing how a packet over 1500 B is rejected:

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.

The "ok" packets, when launching the sample program for three times, were correctly received by the destination device, while all the "wrong" ones were not: Wireshark capture screenshot

Thank you very much in advance.

1
I used to maintain software that did a lot of 802.11 frame generation/injection on Linux and never ran into that issue. Could I see the relevant code?735Tesla
@735Tesla Of course! I just updated the original question with a sample code, extracted from the original one, that you can compile with gcc and that it should reproduce the problem for raw sockets. For what concerns SOCK_DGRAM, instead, the packet is always sent, but it gets fragmented when the size exceeds 1500 B (it can be seen not through a direct error, but for instance when using Wireshark). Thank you very much in advance.es483

1 Answers

0
votes

The question has been solved, in the end, by a user at LinuxQuestions.

I was actually confusing the concept of protocol MTU with the one of interface MTU.

What was limiting the size of the frames transmitted through Wi-Fi was actually the MTU set for the corresponding interface, which can be changed by using, as reported at LinuxQuestions, ip link set <dev> mtu <mtu>. This command can have success if the hardware actually supports the specified MTU or fail if the hardware does not support it.

I was able to successfully transmit bigger frames over Wi-Fi by increasing the MTU set for the wireless interface, until the maximum value of 2304 B.

If I tried, instead, to set an even bigger interface MTU, I got the following error, which was in some way expected:

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

As a final note, it was possible to check the currently set interface MTU, for each active interface, by using, for instance:

ifconfig | grep -i MTU