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 gcc
and 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,ðerHeader,sizeof(struct ether_header));
memcpy(packetok+sizeof(struct ether_header),bufferok,sizeok_final);
// Packet WRONG
memcpy(packetwrong,ðerHeader,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:
Thank you very much in advance.
gcc
and that it should reproduce the problem for raw sockets. For what concernsSOCK_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