linux raw ethernet socket receive more bytes than MTU - linux

I am using the following code snippet,
/* Open PF_PACKET socket, listening for EtherType ETHER_TYPE */
if ((sockfd = socket(PF_PACKET, SOCK_RAW, htons(ETHER_TYPE))) == -1) {
perror("listener: socket");
return -1;
}
/* Set interface to promiscuous mode - do we need to do this every time? */
strncpy(ifopts.ifr_name, ifName, IFNAMSIZ-1);
ioctl(sockfd, SIOCGIFFLAGS, &ifopts);
ifopts.ifr_flags |= IFF_PROMISC;
ioctl(sockfd, SIOCSIFFLAGS, &ifopts);
/* Allow the socket to be reused - incase connection is closed prematurely */
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &sockopt, sizeof sockopt) == -1) {
perror("setsockopt");
close(sockfd);
return ERR_GENERIC;
}
/* Bind to device */
if (setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE, ifName, IFNAMSIZ-1) == -1) {
perror("SO_BINDTODEVICE");
close(sockfd);
return ERR_GENERIC;
}
/* Open RAW socket to send on */
if ((sendfd = socket(AF_PACKET, SOCK_RAW, IPPROTO_IP)) == -1) {
//if ((sendfd = socket(PF_PACKET, SOCK_RAW, htons (ETHER_TYPE))) == -1) {
perror("socket");
}
I am receiving more than MTU size of 1500.
Please share your inputs for getting packet more than size of MTU.

The MTU is the maximum number of bytes the physical layer can transport in a single frame. The packet size is the logical size of the IP packet. If an IP packet does not fit in a single physical frame for transport it will be fragmented for transport (i.e. split into multiple physical frames) and reassembled at the receiver. See wikipedia: IPv4 Fragmentation and reassembly for more details.
What you see in your code is the logical size of the packet which if fragmentation is used can well be larger than the MTU constraint of the physical layer.

Related

multicast: Linux *must not* bind socket to a specific # but Windows *must*

I have a mdns service discovery that uses the following code for initialization
void mdnssd_init(struct in_addr host, bool compliant) {
int sock;
int res;
struct ip_mreq mreq;
struct sockaddr_in addr;
sock = socket(AF_INET, SOCK_DGRAM, 0);
char param = 32;
setsockopt(sock, IPPROTO_IP, IP_MULTICAST_TTL, (void*) &param, sizeof(param));
int enable = 1
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void*) &enable, sizeof(enable));
param = 1;
setsockopt(sock, IPPROTO_IP, IP_MULTICAST_LOOP, (void*) &param, sizeof(param));
#ifndef _WIN32
if (compliant) {
enable = 1;
setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, (void*)&enable, sizeof(enable));
}
#endif
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
/*
* Sending *from * 5353 indicates that we do compliant mDNS query. If we chose
* random ports, the ttl will be much shorter.
*/
if (compliant) addr.sin_port = htons(MDNS_PORT);
// Windows must bind this socket to a specific address, others must not (it's *must*)
#ifdef _WIN32
addr.sin_addr.s_addr = host.s_addr;
#else
addr.sin_addr.s_addr = INADDR_ANY;
#endif
socklen_t addrlen = sizeof(addr);
res = bind(sock, (struct sockaddr *) &addr, addrlen);
if (res < 0) return;
// set outgoing interface for multicast (it's optional, INADDR_ANY could be used)
setsockopt (sock, IPPROTO_IP, IP_MULTICAST_IF, (void*) &host.s_addr, sizeof(host.s_addr));
// set multicast groups we are interested by to receive such packets
memset(&mreq, 0, sizeof(mreq));
mreq.imr_multiaddr.s_addr = inet_addr(MDNS_MULTICAST_ADDRESS);
mreq.imr_interface.s_addr = host.s_addr; // optional, INADDR_ANY can be used
setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (void*) &mreq, sizeof(mreq));
}
It works on all platforms (Linux, Windows, Solaris, FreeBSD, MacOS) but I really don't understand why I need that binding difference between Linux and Windows.
On Linux, if I bind the socket to the address I want to use to send/receive, multicast traffic is not received. Note that queries that require unicast response are properly answered. I understand that some settings here are optional and INADDR_ANY can be used, letting the OS select what interface to send request and receive response (and it works).
On the contrary, on Windows, if the socket is not bound to a specific address, but set to INADDR_ANY, then no multicast traffic is received. Same, queries requiring unicast responses are received.
So it's very puzzling to me that not both options work. I should be able to bind the socket to the address that will be used for sending/receiving - or not. It should work either way, no?

linux multicast join doesn't, why?

I need to receive multicast data from two sources on one ethernet connection, 224.0.31.132 port 14384 and 224.0.31.130 port 14382.
First I tried to bind with port 14384 and then join both ip addresses, but I only get the data from 224.0.31.132.
If I bind with 14382 and join both, I only get the data from 224.0.31.130.
So then I tried to create two sockets, bind the first to 14384 and join 224.0.31.132, then bind the second to 14382 and join 224.0.31.130.
When I do that, I get the data from 224.0.31.130, but not from 224.0.31.132, and ip maddr show shows that the join to 224.0.31.132 has apparently been deleted by the setting up of the second socket.
What do I do to receive data from these two sources on the single eth port?
Here is code for first approach:
/* Create a datagram socket on which to receive. */
int sd = socket(AF_INET, SOCK_DGRAM, 0);
if(sd < 0)
{
perror("Opening datagram socket error");
exit(1);
}
else
cout<<"Opening datagram socket....OK."<<endl;
/* Enable SO_REUSEADDR to allow other */
/* applications to receive copies of the multicast datagrams. */
{
int reuse = 1;
if(setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, (char *)&reuse, sizeof(reuse)) < 0)
{
perror("Setting SO_REUSEADDR error");
close(sd);
exit(1);
}
else
cout<<"Setting SO_REUSEADDR...OK."<<endl;
}
/* Enable SO_REUSEPORT to allow multiple uses of receive port */
{
int reuse = 1;
if(setsockopt(sd, SOL_SOCKET, SO_REUSEPORT, (char *)&reuse, sizeof(reuse)) < 0)
{
perror("Setting SO_REUSEPORT error");
close(sd);
exit(1);
}
else
cout<<"Setting SO_REUSEPORT...OK."<<endl;
}
/* Bind to the proper port number with the IP address */
/* specified as INADDR_ANY. */
struct sockaddr_in localSock;
memset((char *) &localSock, 0, sizeof(localSock));
localSock.sin_family = AF_INET;
localSock.sin_port = htons(14384);
localSock.sin_addr.s_addr = INADDR_ANY;
if(bind(sd, (struct sockaddr*)&localSock, sizeof(localSock)))
{
perror("Binding datagram socket error");
close(sd);
exit(1);
}
else
cout<<"Binding datagram socket...OK."<<endl;
/* Join the multicast groups on the local nic interface. */
struct ip_mreq group;
group.imr_interface.s_addr = inet_addr("0.0.0.0");
int ipcount = stoi(getSetup("mcstcount"));
while(ipcount>0)
{
string which = "listenip" + to_string(ipcount);
cout<<"listen to "<<getSetup(which.c_str())<<endl;
group.imr_multiaddr.s_addr = inet_addr(getSetup(which.c_str()).c_str());
ipcount--;
if(setsockopt(sd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&group, sizeof(group)) < 0)
{
perror("Adding multicast group error");
close(sd);
exit(1);
}
else
cout<<"Adding multicast group...OK."<<endl;
}
// process Mdp feed
while( !stop_cond )
{
/* Read from the socket. */
const int DBUFSZ = 5000;
uint8_t databuf[DBUFSZ];
int datalen;
datalen=read(sd, databuf, DBUFSZ);
if(datalen < 0)
{
perror("Reading datagram message error");
close(sd);
exit(1);
}
//process data
}
Here is resulting output:
Opening datagram socket....OK.
Setting SO_REUSEADDR...OK.
Setting SO_REUSEPORT...OK.
Binding datagram socket...OK.
listen to 224.0.33.80
Adding multicast group...OK.
listen to 224.0.31.130
Adding multicast group...OK.
listen to 224.0.31.132
Adding multicast group...OK.
But data only comes in from 224.0.31.132.
Concluded one cannot use a single socket as there are multiple ports to receive from. Went back to approach two, using multiple sockets. Got it working, not sure what was wrong the first time.

pfring could not capture inbound packets on Ethernet of Raspberry Pi 3 B

I have installed PFRING-6.6.0 (loaded "pf_ring.ko") on my x86_64 machine running Ubuntu 14.04 to capture all incoming packets on "eth0" whose source or destination port is "2404" (see the code below). and the code is working fine. The following code creates a pfring socket with given BPF filter and the socket should capture only incoming "2404" packets
But my problem is though I have installed the same PFRING on my Raspberry Pi 3 B machine running Ubuntu-mate 16.04, the same code is not able to capture the incoming packets. (I have changed the device name to "eth0"). is this architecture related problem ?.. how resolve this ?.
char *device = "eth0";
pfring *pd;
int main(int argc, char *argv[]) {
/* hard coaded filters */
char *bpfFilter "(ip host 10.180.6.105 && ip host 10.180.5.179) && tcp port 2404";
u_int32_t flags = 0;
int i = 0;
flags |= PF_RING_REENTRANT;
flags |= PF_RING_PROMISC;
flags |= PF_RING_HW_TIMESTAMP;
flags |= PF_RING_STRIP_HW_TIMESTAMP;
flags |= PF_RING_CHUNK_MODE;
flags |= PF_RING_IXIA_TIMESTAMP;
pd = pfring_open(device, 256, flags);
if (pd == NULL) {
fprintf(stderr, "pfring_open error [%s] (pf_ring not loaded or interface %s is down ?)\n",
strerror(errno), device);
exit(0);
}
if ((pfring_set_direction(pd, 1)) != 0) /* 0=RX+TX, 1=RX only, 2=TX only */
fprintf (stderr, "capture direction not set\n");
if ((pfring_set_socket_mode(pd, recv_only_mode)) != 0)
fprintf(stderr, "pfring_set_socket_mode unsuccessfull\n");
if ((pfring_set_bpf_filter(pd, bpfFilter)) < 0)
fprintf(stderr, "pfring_set_bpf_filter unsuccessfull\n");
else
fprintf(stderr, "set_bpf_filter successfull\n");
pfring_set_poll_duration(pd, 500);
if (pfring_enable_ring(pd) != 0) {
printf("Failed to enable ring :-(\n");
pfring_close(pd);
}
while(1) {
if ((ret = pfring_is_pkt_available(pd)) == 0) {
printf("No incomming packet %d\n");
continue;
}
if ((ret = pfring_loop(pd[RTUnum], RTUProcesssPacket, (u_char*)&RTUnum, 0)) != 0) {
fprintf(stderr, "Failed to capture packet\n");
sleep(1);
}
}
}
void RTUProcesssPacket(const struct pfring_pkthdr *h,
const u_char *packet, const u_char *user_bytes) {
log packets into pcap file;
parse the packet;
apply IDS rules();
}
OUTPUT:
(ip host 10.180.6.105 && ip host 10.180.5.179) && tcp port 2404
set_bpf_filter successfull
No incomming packet
No incomming packet
No incomming packet
No incomming packet
No incomming packet
from what i understand, the rpi is a 64bit architecture but raspian os is only 32bit

Handsfree device not able to receive any audio from sco socket

I am developing a handsfree module, in which after completing service level connection I am setting up sco connection with phone's audio gateway and receive audio data as below...
void audio_connection_setup(char *bluetooth_addr)
{
struct sockaddr_sco addr;
pthread_t tid;
int scoSock = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO);
memset(&addr, 0, sizeof(addr));
addr.sco_family = AF_BLUETOOTH;
addr.sco_bdaddr = *BDADDR_ANY;
if (bind(scoSock, (struct sockaddr *) &addr, sizeof(addr)) < 0)
{
printf( "Can't bind socket: %s (%d)",strerror(errno), errno);
}
hci_read_voice_setting(scoSock, &voice, 5000);
hci_write_voice_setting(scoSock, BT_VOICE_CVSD_16BIT, 5000);
str2ba(bluetooth_addr, &addr.sco_bdaddr);
if (connect(scoSock, (struct sockaddr *) &addr, sizeof(addr)) < 0)
{
printf( "\nCan't connect: %s (%d)", strerror(errno), errno);
}
pthread_create(&tid, NULL, &read_data, &scoSock);
}
Here is the read_data thread
gBuff[16284];
void* read_data(int *scoSock)
{
int fd = *scoSock;
int len = -1;
char buff[48];
int numBytesRead;
while (1)
{
numBytesRead = 0;
while(numBytesRead < 16284)
{
memset(buff, 0x0, 48);
len = recv(fd, buff, 48, MSG_DONTWAIT);
usleep(10);
memcpy(gBuff + numBytesRead, buff + 2, len - 2);
numBytesRead = numBytesRead + len - 2;
}
printf("Number of bytes received = %d errno = %d\n", numBytesRead, errno);
memset(gBuff, 0x0, numBytesRead);
}
}
This code is working fine if I run it on linux PC, But when i run on arm board the recv system call returns errno EAGAIN in continuous loop and never comes out. On PC the recv system call returns number of bytes properly. What may be possible cause to this issue?
I think the reason should be when your code runs on the pc, you may get the audio data from HCI link, i.e. the HF sco over HCI and then you transfer it to PC driver. However when you switch to board, I wonder whether the board's hardware Bluetooth interface lets say UART, whether it has high throughput to transfer the sco data, and most importantly, you need check whether the sco data was routed to PCM interface i.e. does not sending to HCI.

Using BPF with SOCK_DGRAM on Linux machine

Is it possible to filter packets using BPF on datagram socket?
No error occures when I try to attach a filter, but I don't receive any packet.
I compiled a filter using libpcap, and the filter works with tcpdump.
Here is shortened version of my code:
static const char filter[] = "udp[8] == 0x00";
int sock = socket(AF_INET, SOCK_DGRAM, 0);
pcap_t *pcap = pcap_open_dead(DLT_RAW, 1024);
struct bpf_program bpf_prog;
pcap_compile(pcap, &bpf_prog, filter, 0, PCAP_NETMASK_UNKNOWN);
struct sock_fprog linux_bpf = {
.len = bpf_prog.bf_len,
.filter = (struct sock_filter *) bpf_prog.bf_insns,
};
setsockopt(sock, SOL_SOCKET, SO_ATTACH_FILTER, &linux_bpf, sizeof(linux_bpf));
My machine is ubuntu 12.04 x86.
well, after some tests and trials, it is possible.
however, libpcap does not support it directly.
what should be done is open a pcap handler specifying ethernet data type, and then access the bytes in the udp packet as if you access the ethernet packet.
the filter offsets start from the beginning of the packet, but the 'packet' depends on the layer you opened the socket for.
if one opens socket with SOCK_DGRAM, the bpf instruction ldb 0 will load the first byte of the udp header. so when accessing ether[0] in the filter libpcap will compile it to ldb 0 which is what we want.
so, the corrected code should be something like this:
static const char filter[] = "ether[8] == 0x00";
int sock = socket(AF_INET, SOCK_DGRAM, 0);
pcap_t *pcap = pcap_open_dead(DLT_EN10MB, 1024);
struct bpf_program bpf_prog;
pcap_compile(pcap, &bpf_prog, filter, 0, PCAP_NETMASK_UNKNOWN);
struct sock_fprog linux_bpf = {
.len = bpf_prog.bf_len,
.filter = (struct sock_filter *) bpf_prog.bf_insns,
};
setsockopt(sock, SOL_SOCKET, SO_ATTACH_FILTER, &linux_bpf, sizeof(linux_bpf));

Resources