Using BPF with SOCK_DGRAM on Linux machine - linux

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));

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?

How can I send more bytes using Bluez L2CAP?

I have the following code that connects to another machine using BlueZ and sends packets:
struct sockaddr_l2 addr = { 0 };
int s, status;
char dest[18] = "DC:FB:48:6B:BF:0B";
int socket1;
int32_t value = 0;
// allocate a socket
socket1 = socket(AF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);
// set the connection parameters (who to connect to)
addr.l2_family = AF_BLUETOOTH;
addr.l2_psm = htobs(0x1001);
str2ba( dest, &addr.l2_bdaddr );
status = connect(socket1, (struct sockaddr *)&addr, sizeof(addr));
while( status != 0)
{
status = connect(socket1, (struct sockaddr *)&addr, sizeof(addr));
std::cout << "Waiting for DC:FB:48:6B:BF:0B" << std::endl;
sleep(2);
}
while (true)
{
double data[512] = {0.0};
memset(data, 0, 512);
status = write(socket1, data, 512);
}
As you can see, I just send 512 bytes and other machine reads them just fine. However, when I try to increase to 1000 bytes, other machine can no longer accept any bytes and just does nothing.
How can I send more bytes in this case? I am using Linux CentOS 8.
By default the maximum transmission rate of L2CAP is 672 bytes. I would recommend you to try setting the maximum transmission unit to your required value.
Have a look at "4.3.1 Maximum Transmission Unit" here.

In Linux kernel TCP implement, Why "sk_acceptq_is_full" use '>' rather than '>='?

My linux kernel version is 4.4.0.
Here is my code snippet
int listenfd = socket(PF_INET, SOCK_STREAM, 0);
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(50001);
bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
listen(listenfd, 2); // just listen, never accept
while(1)
{
sleep(1);
}
I expect it can only accomplish 2 TCP 3-shakehand process because the backlog argument in listen is 2.
However, when I do a test, the result is 3 rather than 2
So, I had a glance over the source code.
In tcp_input.c
int tcp_conn_request(struct request_sock_ops *rsk_ops,
const struct tcp_request_sock_ops *af_ops,
struct sock *sk, struct sk_buff *skb)
{
// code omitted
if (sk_acceptq_is_full(sk) && inet_csk_reqsk_queue_young(sk) > 1) {
NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_LISTENOVERFLOWS);
goto drop;
}
// code omitted
}
it use sk_acceptq_is_full to see if the accept queue is full
static inline bool sk_acceptq_is_full(const struct sock *sk)
{
return sk->sk_ack_backlog > sk->sk_max_ack_backlog;
}
I knew that the sk->sk_ack_backlog is the number of complete TCP 3-shakehand that is not accept() by user.And sk->sk_max_ack_backlog is the argument is listen()
So It seems that i found out the reason why result is 3 rather than 2,
But what puzzles me is that why he use > rather than >= to judge the queue is full ? Isn't more reasonable to use >= ?
the new version(5.0.9) kernel is same.

implementing LWIP multicast on STM32F7 + FreeRTOS?

I have a client/server LWIP program that works correctly with unicast communication however I want to use multicast features so I used IGMP library did the following:
1- in lwipopts.h:
#define LWIP_IGMP 1 //allowed IGMP
2- in ethernetif.c:
netif->flags |= NETIF_FLAG_IGMP; //in low_level_init function
3-in my source file (for both client and server projects):
implemented the following code:
void recCallBack (void)
{
printf("connected"); //BREAK_POINT
}
static void UDP_Multicast_init(void *arg)
{
struct ip4_addr ipgroup, localIP;
struct udp_pcb *g_udppcb;
char msg[] = "hello";
struct pbuf* p;
p = pbuf_alloc(PBUF_TRANSPORT,sizeof(msg),PBUF_RAM);
memcpy (p->payload, msg, sizeof(msg));
IP4_ADDR(&ipgroup, 224, 0, 1, 129 ); //Multicast IP address.
IP4_ADDR(&localIP, 192, 168, 1, 2); //Interface IP address
#if LWIP_IGMP
s8_t iret = igmp_joingroup((ip4_addr_t *)(&localIP),(ip4_addr_t *)(&ipgroup));
#endif
g_udppcb =( struct udp_pcb*) udp_new();
udp_bind(g_udppcb, &localIP, 319); //to allow receiving multicast
udp_recv(g_udppcb, recCallBack,NULL); //recCallBack is the callback function that will be called every time you receive multicast
udp_sendto(g_udppcb,p,&ipgroup,319); //send a multicast packet
}
void telnet_shell_init(void)
{
sys_thread_new("TELNET", UDP_Multicast_init, NULL, DEFAULT_THREAD_STACKSIZE, osPriorityAboveNormal);
}
The result: all the mentioned code steps are executed successfully in both projects (client and server) but I'm not receiving any multicast messages (or maybe not even sending)!
I added a "BREAK_POINT" in the callback function but I never reached it. Can you help me? either by suggesting a solution or at least a way to track the problem... I'm using STM32F746 Nucleo board with LWIP, FreeRTOS libraries generated by cubeMX.
Thank you.
<<< Edit >>>
After more investigations I found out that the problem is in the reception of the multi-cast frames which should be enabled during the MAC initialization. Although the following code did not work for me, it was helpful to others so here it is:
4- in the stm32f7xx_hal_eth.c (ETH_MACDMAConfig function):
macinit.PromiscuousMode = ETH_PROMISCUOUS_MODE_ENABLE;
macinit.MulticastFramesFilter = ETH_MULTICASTFRAMESFILTER_NONE;
My multicast testing was finished successfully with STM32F407 and CubeMX version 4.25.0.
The Kalkhouri's question was helpful.
I share my working code here.
Following code must be included same as Kalkhouri did.
#define LWIP_IGMP 1
macinit.MulticastFramesFilter = ETH_MULTICASTFRAMESFILTER_NONE;
netif->flags |= NETIF_FLAG_IGMP;
I used socket API of LWIP rather than low level function.
#include "lwip/opt.h"
#include "lwip/dhcp.h"
#include "lwip/netif.h"
#include "lwip/tcpip.h"
#include "lwip/sockets.h"
int Bind(int sock, uint16_t port)
{
struct sockaddr_in serv_addr;
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(port);
if (bind(sock, (struct sockaddr *) &serv_addr, (socklen_t)sizeof(serv_addr)) < 0)
return -1;
return 0;
}
int JoinGroup(int sock, const char* join_ip, const char* local_ip)
{
ip_mreq mreq;
mreq.imr_multiaddr.s_addr = inet_addr(join_ip);
mreq.imr_interface.s_addr = inet_addr(local_ip);
if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&mreq, sizeof(mreq)) < 0)
return -1;
return 0;
}
void MulticastStart()
{
int sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
Bind(sock, 5000);
JoinGroup(sock, "224.1.1.1", "192.168.10.123");
// Now you can do recvfrom() in RTOS task.
........
}
Note: You should use this code under the RTOS support.

Linux : how to set default route from C?

How can I set (and replace the existing) default network route from a C program? I'd like to do it without shell commands if possible (this is a low memory embedded system). Also can you set the default route without specifying the gateway IP address? In my application I want to make either ppp0 or eth0 the default route, depending on whether the cable is plugged into eth0 or not.
Thanks,
Fred
You can make IOCTL calls to set the default route from a C program.
void main()
{
int sockfd;
struct rtentry rt;
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd == -1)
{
perror("socket creation failed\n");
return;
}
struct sockaddr_in *sockinfo = (struct sockaddr_in *)&rt.rt_gateway;
sockinfo->sin_family = AF_INET;
sockinfo->sin_addr.s_addr = inet_addr("Your Address");
sockinfo = (struct sockaddr_in *)&rt.rt_dst;
sockinfo->sin_family = AF_INET;
sockinfo->sin_addr.s_addr = INADDR_ANY;
sockinfo = (struct sockaddr_in *)&rt.rt_genmask;
sockinfo->sin_family = AF_INET;
sockinfo->sin_addr.s_addr = INADDR_ANY;
rt.rt_flags = RTF_UP | RTF_GATEWAY;
rt.rt_dev = "eth0";
if(ioctl(sockfd, SIOCADDRT, &rt) < 0 )
perror("ioctl");
return;
}
You could strace the route command you are wanting to mimic. This gives you the relevant syscalls useful to change routing.
You may be interested by the proc(5) interface, e.g. its /proc/net/route pseudo-file.
See also ip(7).

Resources