implementing LWIP multicast on STM32F7 + FreeRTOS? - multicast

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.

Related

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.

Can I (IGMP) join a stream on two NICs and answer (IGMP) queries on both NICs in Linux?

I made a Linux application to receive multicast traffic. It works when I connect to one interface. When I connect to a stream, in Wireshark, I see an IGMP join, and when the switch sends IGMP queries, Linux replies with an IGMP report for the stream.
However, I need more bandwidth than my one interface can provide. To have more bandwidth, I have multiple interfaces on the same network. I therefore duplicated my code to have two interfaces connect to a stream. In that case, in Wireshark, I see an IGMP join on both interfaces, but when the switch sends IGMP queries, Linux only replies with an IGMP report on one interface. Therefore, the switch timeout occurs and I lose the stream on the interface that is not reporting.
Here is a reproducible example. It doesn't receive any data, but it is enough to see the problem happen in Wireshark:
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
// Create and fill internet socket address structure for both sockets.
struct sockaddr_in internetSocketAdressStructure;
bzero(&internetSocketAdressStructure, sizeof(internetSocketAdressStructure));
internetSocketAdressStructure.sin_family = AF_INET;
internetSocketAdressStructure.sin_addr.s_addr=htonl(INADDR_ANY);
internetSocketAdressStructure.sin_port = htons(10000);
// Create first socket.
int firstSocketToUse;
if ((firstSocketToUse = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
perror("First socket failed");
exit(1);
}
// Set first socket for address reuse.
int reuseAddressForFirstSocket;
reuseAddressForFirstSocket = 1;
if (setsockopt(firstSocketToUse, SOL_SOCKET, SO_REUSEADDR, ( char* )&reuseAddressForFirstSocket, sizeof(reuseAddressForFirstSocket) ) == -1 ) {
perror("Error setting first socket for address reuse");
exit(1);
}
// Bind first socket.
if (bind(firstSocketToUse, (struct sockaddr *)&internetSocketAdressStructure, sizeof(internetSocketAdressStructure))==-1) {
perror("First bind failed");
exit(1);
}
// Join stream on first socket.
struct ip_mreq multicastRequestOnFirstInterface;
multicastRequestOnFirstInterface.imr_multiaddr.s_addr = inet_addr("239.120.15.2");
multicastRequestOnFirstInterface.imr_interface.s_addr = inet_addr("25.25.40.116");
if (setsockopt(firstSocketToUse, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&multicastRequestOnFirstInterface, sizeof(multicastRequestOnFirstInterface)) == -1) {
perror("Error joining multicast group on Interface 1");
exit(1);
}
// Create second socket.
int secondSocketToUse;
if ((secondSocketToUse = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
perror("Second socket failed");
exit(1);
}
// Set second socket for address reuse.
int reuseAddressForSecondSocket;
reuseAddressForSecondSocket = 1;
if (setsockopt(secondSocketToUse, SOL_SOCKET, SO_REUSEADDR, ( char* )&reuseAddressForSecondSocket, sizeof(reuseAddressForSecondSocket) ) == -1 ) {
perror("Error setting second socket for address reuse");
exit(1);
}
// Bind second socket.
if (bind(secondSocketToUse, (struct sockaddr *)&internetSocketAdressStructure, sizeof(internetSocketAdressStructure))==-1) {
perror("Second bind failed");
exit(1);
}
// Join stream on second socket.
struct ip_mreq multicastRequestOnSecondInterface;
multicastRequestOnSecondInterface.imr_multiaddr.s_addr = inet_addr("239.120.15.2");
multicastRequestOnSecondInterface.imr_interface.s_addr = inet_addr("25.25.40.134");
if (setsockopt(secondSocketToUse, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&multicastRequestOnSecondInterface, sizeof(multicastRequestOnSecondInterface)) == -1) {
perror("Error joining multicast group on Interface 2");
exit(1);
}
// Wait forever.
while(1) {}
}
I saw a post on Stackoverflow suggesting to do this with only one socket, so I tried that, but the same issue occurs. Here is the code for that:
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
// Create and fill internet socket address structure for the socket.
struct sockaddr_in internetSocketAdressStructure;
bzero(&internetSocketAdressStructure, sizeof(internetSocketAdressStructure));
internetSocketAdressStructure.sin_family = AF_INET;
internetSocketAdressStructure.sin_addr.s_addr=htonl(INADDR_ANY);
internetSocketAdressStructure.sin_port = htons(10000);
// Create socket.
int socketToUse;
if ((socketToUse = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
perror("socket failed");
exit(1);
}
// Bind socket.
if (bind(socketToUse, (struct sockaddr *)&internetSocketAdressStructure, sizeof(internetSocketAdressStructure))==-1) {
perror("bind failed");
exit(1);
}
// Set first socket for address reuse.
int reuseAddress;
reuseAddress = 1;
if (setsockopt(socketToUse, SOL_SOCKET, SO_REUSEADDR, ( char* )&reuseAddress, sizeof(int) ) == -1 ) {
perror("Error setting socket for address reuse");
exit(1);
}
// Join stream on first interface.
struct ip_mreq multicastRequestOnFirstInterface;
multicastRequestOnFirstInterface.imr_multiaddr.s_addr = inet_addr("239.120.15.2");
multicastRequestOnFirstInterface.imr_interface.s_addr = inet_addr("25.25.40.116");
if (setsockopt(socketToUse, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&multicastRequestOnFirstInterface, sizeof(multicastRequestOnFirstInterface)) == -1)
{
perror("Error joining multicast group on first interface");
exit(1);
}
// Join stream on second interface.
struct ip_mreq multicastRequestOnSecondInterface;
multicastRequestOnSecondInterface.imr_multiaddr.s_addr = inet_addr("239.120.15.2");
multicastRequestOnSecondInterface.imr_interface.s_addr = inet_addr("25.25.40.134");
if (setsockopt(socketToUse, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&multicastRequestOnSecondInterface, sizeof(multicastRequestOnSecondInterface)) == -1)
{
perror("Error joining multicast group on second interface");
exit(1);
}
// Wait forever.
while(1) {}
}
I've also tried to put the interfaces in promiscuous mode:
sudo ip link set interface1 promisc on
sudo ip link set interface2 promisc on
and add the noprefixroute option to each interface:
sudo ip addr change 25.25.40.134 dev interface1 noprefixroute
sudo ip addr change 25.25.40.116 dev interface2 noprefixroute
Both those things failed to solve my problem.
That being said, I found sources (1, 2) that might indicate that what I'm trying to do is impossible, though these sources seem somewhat old.
I was able to fix the issue by putting both ports on the switch to different vlans, though that is a clunky/awful solution that might not be acceptable.
Is connecting to multicast streams on the same network through two interfaces and configure the network to answer the IGMP queries on both interfaces possible?
It is explained as clear as day here:
https://access.redhat.com/solutions/53031
And less clearly here:
https://www.kernel.org/doc/Documentation/networking/ip-sysctl.txt
Considering a computer with 2 net interfaces, interfaceA and interfaceB.
Considering that Linux decides to use interfaceB to send packets to ip address X.
Considering a packet that is received on interfaceA from ip address X.
Linux will drop the packet.
Unless you run
sysctl net.ipv4.conf.all.rp_filter=2 in a terminal or add that line to /etc/sysctl.conf.
It enables receiving packets from an ip address on other interfaces than the one it uses to send packets to that ip address!

Select MAC address for binding to a specific bluetooth adapter

The following piece of code binds a Bluetooth socket. My expectation is that the call to bind() (or to listen()) with that MAC address as argument fails, because when using AF_INET sockets, I cannot bind() to an IP address that I don't have. This answer suggests that it's possible to select your adapter, but I cannot reproduce that in my experiments.
/* From https://people.csail.mit.edu/albert/bluez-intro/x502.html */
#include <stdio.h>
#include <unistd.h>
#include <sys/socket.h>
#include <bluetooth/bluetooth.h>
#include <bluetooth/rfcomm.h>
int main(int argc, char **argv)
{
struct sockaddr_rc loc_addr = { 0 }, rem_addr = { 0 };
char buf[1024] = { 0 };
int s, client, bytes_read;
socklen_t opt = sizeof(rem_addr);
// allocate socket
s = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
// bind socket to port 1 of the first available
// local bluetooth adapter
loc_addr.rc_family = AF_BLUETOOTH;
// loc_addr.rc_bdaddr = *BDADDR_ANY;
bdaddr_t my_bdaddr = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06};
loc_addr.rc_bdaddr = my_bdaddr;
loc_addr.rc_channel = (uint8_t) 1;
{
int res = bind(s, (struct sockaddr *)&loc_addr, sizeof(loc_addr));
char baddr[128];
ba2str(&loc_addr.rc_bdaddr, baddr);
fprintf(stderr, "bound to %s: %d\n", baddr, res);
}
// put socket into listening mode
{
int res = listen(s, 1);
fprintf(stderr, "Listening: %d\n", res);
}
// accept one connection
client = accept(s, (struct sockaddr *)&rem_addr, &opt);
ba2str( &rem_addr.rc_bdaddr, buf );
fprintf(stderr, "accepted connection from %s\n", buf);
memset(buf, 0, sizeof(buf));
// read data from the client
bytes_read = read(client, buf, sizeof(buf));
if( bytes_read > 0 ) {
printf("received [%s]\n", buf);
}
// close connection
close(client);
close(s);
return 0;
}
So I guess my question is: Is my expectation of the bind() having to fail wrong?
The main reason I am concerned with the MAC address is that I want another party to connect to me and send it some data. For the other party to establish a connection, it needs my MAC address and port. Now I can find my MAC address via the DBus API (or probably via some other means) but then I'm afraid that it's racy, e.g. I cannot be sure that the MAC address I queried is the same that I've bound to, because in the time between the query and the bind the user may have removed their Bluetooth adapter and inserted a new one. And before I jump through the hoops of subscribing to BlueZ DBus signals I thought I'd use the MAC for binding the socket.

Linux select() and FIFO ordering of multiple sockets?

Is there any way for the Linux select() call relay event ordering?
A description of what I'm seeing:
On one machine, I wrote a simple program which sends three multicast packets, one to each of three different multicast groups. These packets are sent back-to-back, with no delay in between. I.e. sendto(mcast_group1); sendto(mcast_group2); sendto(mcast_group3).
On the other machine, I have a receiving program. The program uses one socket per multicast group. Each socket does a bind() and IP_ADD_MEMBERSHIP (i.e. join/subscribe) to the address to which it listens. The program then does a select() on the three sockets.
When select returns, all three sockets are available for reading. But which one came first? The ready-for-reading list of sockets is a set, and therefore has no order. What I would like is if select() returned exactly once per received packet, in order (the increased overhead is acceptable here). Or, is there some other kind of mechanism I can use to determine packet receive order?
Additional information:
OS is CentOS 5 (effectively Redhat Enterprise Linux) on x86_64
NIC hardware is an Intel 82571EB
I've tried e1000e driver versions 1.3.10-k2 and 2.1.4-NAPI
I've tried pinning the NIC's interrupt to an unloaded and isolated CPU core
I've disabled hardware IRQ coalescing via setting the driver option InterruptThrottleRate=0, and setting rx-usecs=0 via ethtool
I also tried using epoll, and it has the same behavior
A final remark: packet ordering is preserved if I only use one socket. In this case, I bind to INADDR_ANY (0.0.0.0) and do the IP_ADD_MEMBERSHIP multiple times on the same socket. But this does not work for our application, because we need the filtering provided by binding to the actual multicast address. Ultimately, there will be multiple multicast receiving programs on the same machine, with subscription sets that may intersect with each other. So maybe an alternate solution is to find another way to achieve the filtering effect of bind(), but without bind().
You can use IP_PKTINFO to get the address of the multicast group the packet was send to - even if the socket is subscribed for a bunch of multicast groups. Having this in place, you will get the packets in order and the ability to filter by group addresses. See the example below:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/stat.h>
#include <ctype.h>
#include <errno.h>
#define PORT 1234
#define PPANIC(msg) perror(msg); exit(1);
#define STATS_PATCH 0
int main(int argc, char **argv)
{
fd_set master;
fd_set read_fds;
struct sockaddr_in serveraddr;
int sock;
int opt = 1;
size_t i;
int rc;
char *mcast_groups[] = {
"226.0.0.1",
"226.0.0.2",
NULL
};
#if STATS_PATCH
struct stat stat_buf;
#endif
struct ip_mreq imreq;
FD_ZERO(&master);
FD_ZERO(&read_fds);
rc = sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if(rc == -1)
{
PPANIC("socket() failed");
}
rc = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
if(rc == -1)
{
PPANIC("setsockopt(reuse) failed");
}
memset(&serveraddr, 0, sizeof(serveraddr));
serveraddr.sin_family = AF_INET;
serveraddr.sin_port = htons(PORT);
serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
rc = bind(sock, (struct sockaddr *)&serveraddr, sizeof(serveraddr));
if(rc == -1)
{
PPANIC("bind() failed");
}
rc = setsockopt(sock, IPPROTO_IP, IP_PKTINFO, &opt, sizeof(opt));
if(rc == -1)
{
PPANIC("setsockopt(IP_PKTINFO) failed");
}
for (i = 0; mcast_groups[i] != NULL; i++)
{
imreq.imr_multiaddr.s_addr = inet_addr(mcast_groups[i]);
imreq.imr_interface.s_addr = INADDR_ANY;
rc = setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (const void *)&imreq, sizeof(struct ip_mreq));
if (rc != 0)
{
PPANIC("joing mcast group failed");
}
}
FD_SET(sock, &master);
while(1)
{
read_fds = master;
rc = select(sock + 1, &read_fds, NULL, NULL, NULL);
if (rc == 0)
{
continue;
}
if(rc == -1)
{
PPANIC("select() failed");
}
if(FD_ISSET(sock, &read_fds))
{
char buf[1024];
int inb;
char ctrl_msg_buf[1024];
struct iovec iov[1];
iov[0].iov_base = buf;
iov[0].iov_len = 1024;
struct msghdr msg_hdr = {
.msg_iov = iov,
.msg_iovlen = 1,
.msg_name = NULL,
.msg_namelen = 0,
.msg_control = ctrl_msg_buf,
.msg_controllen = sizeof(ctrl_msg_buf),
};
struct cmsghdr *ctrl_msg_hdr;
inb = recvmsg(sock, &msg_hdr, 0);
if (inb < 0)
{
PPANIC("recvmsg() failed");
}
for (ctrl_msg_hdr = CMSG_FIRSTHDR(&msg_hdr); ctrl_msg_hdr != NULL; ctrl_msg_hdr = CMSG_NXTHDR(&msg_hdr, ctrl_msg_hdr))
{
if (ctrl_msg_hdr->cmsg_level == IPPROTO_IP && ctrl_msg_hdr->cmsg_type == IP_PKTINFO)
{
struct in_pktinfo *pckt_info = (struct in_pktinfo *)CMSG_DATA(ctrl_msg_hdr);
printf("got data for mcast group: %s\n", inet_ntoa(pckt_info->ipi_addr));
break;
}
}
printf("|");
for (i = 0; i < inb; i++)
printf("%c", isprint(buf[i])?buf[i]:'?');
printf("|\n");
#if STATS_PATCH
rc = fstat(sock, &stat_buf);
if (rc == -1)
{
perror("fstat() failed");
} else {
printf("st_atime: %d\n", stat_buf.st_atime);
printf("st_mtime: %d\n", stat_buf.st_mtime);
printf("st_ctime: %d\n", stat_buf.st_ctime);
}
#endif
}
}
return 0;
}
the code below won't solve OPs problem but may guide people dealing with similar requirements
(EDIT) One should not do such things late at night... even with that solution you will only get the order the fd was handled by select - and this will give you no indication about the time of the frame arrival.
As stated here, it is currently not possible to retrieve the order of the sockets or the timestamps they changed as the required callback is not set for socket inodes. But if you are able to patch your kernel, you may work around the problem by setting the time within the select system call.
The following patch may give you an idea:
diff --git a/fs/select.c b/fs/select.c
index 467bb1c..3f2927e 100644
--- a/fs/select.c
+++ b/fs/select.c
## -435,6 +435,9 ## int do_select(int n, fd_set_bits *fds, struct timespec *end_time)
for (i = 0; i < n; ++rinp, ++routp, ++rexp) {
unsigned long in, out, ex, all_bits, bit = 1, mask, j;
unsigned long res_in = 0, res_out = 0, res_ex = 0;
+ struct timeval tv;
+
+ do_gettimeofday(&tv);
in = *inp++; out = *outp++; ex = *exp++;
all_bits = in | out | ex;
## -452,6 +455,16 ## int do_select(int n, fd_set_bits *fds, struct timespec *end_time)
f = fdget(i);
if (f.file) {
const struct file_operations *f_op;
+ struct kstat stat;
+
+ int ret;
+ u8 is_sock = 0;
+
+ ret = vfs_getattr(&f.file->f_path, &stat);
+ if(ret == 0 && S_ISSOCK(stat.mode)) {
+ is_sock = 1;
+ }
+
f_op = f.file->f_op;
mask = DEFAULT_POLLMASK;
if (f_op->poll) {
## -464,16 +477,22 ## int do_select(int n, fd_set_bits *fds, struct timespec *end_time)
res_in |= bit;
retval++;
wait->_qproc = NULL;
+ if(is_sock && f.file->f_inode)
+ f.file->f_inode->i_ctime.tv_sec = tv.tv_sec;
}
if ((mask & POLLOUT_SET) && (out & bit)) {
res_out |= bit;
retval++;
wait->_qproc = NULL;
+ if(is_sock && f.file->f_inode)
+ f.file->f_inode->i_ctime.tv_sec = tv.tv_sec;
}
if ((mask & POLLEX_SET) && (ex & bit)) {
res_ex |= bit;
retval++;
wait->_qproc = NULL;
+ if(is_sock && f.file->f_inode)
+ f.file->f_inode->i_ctime.tv_sec = tv.tv_sec;
}
/* got something, stop busy polling */
if (retval) {
Notes:
this is... just for you :) - don't expect it in the mainline
do_gettimeofday() is called before each relevant fd is tested.
to get higher granularity this should be done in each iteration (and only if needed). since the stat-interface only offers a granularity of one second
you may (!UGLY!) use the remaining time attributes to map the fractions of a second to those fields.
this was done using kernel 3.16.0 and is not well tested. don't use it in a space ship or medical equipment. if you would like to try it, get a filesystem-image (eg. https://people.debian.org/~aurel32/qemu/amd64/debian_wheezy_amd64_standard.qcow2) and use qemu to test it:
sudo qemu-system-x86_64 -kernel arch/x86/boot/bzImage -hda debian_wheezy_amd64_standard.qcow2 -append "root=/dev/sda1"
If select() returns > 1 the events must have been so close together as to make the question of ordering meaningless.
You can obtain the timestamp at which a file descriptor became ready using fstat.
For more info read http://pubs.opengroup.org/onlinepubs/009695399/functions/fstat.html

UDP-Broadcast on all interfaces

On a Linux system with a wired and a wireless interface (e.g. 192.168.1.x and 192.168.2.x subnets) I want to send a UDP broadcast that goes out via ALL available interfaces (i.e. both through the wired and the wireless interface).
Currently I sendto() to INADDR_BROADCAST, however it seems that the broadcast only is sent through one of the interfaces (not always the same and subsequent broadcasts may use the other interface).
Is there a way that I can send a UDP broadcast that goes out through every single interface?
First of all, you should consider broadcast obsolete, specially INADDR_BROADCAST (255.255.255.255). Your question highlights exactly one of the reasons that broadcast is unsuitable. It should die along with IPv4 (hopefully). Note that IPv6 doesn't even have a concept of broadcast (multicast is used, instead).
INADDR_BROADCAST is limited to the local link. Nowadays, it's only visible use is for DHCP auto-configuration, since at such time, the client will not know yet in what network it is connected to.
With a single sendto(), only a single packet is generated, and the outgoing interface is determined by the operating system's routing table (ip route on linux). You can't have a single sendto() generate more than one packet, you would have to iterate over all interfaces, and either use raw sockets or bind the socket to a device using setsockopt(..., SOL_SOCKET, SO_BINDTODEVICE, "ethX") to send each packet bypassing the OS routing table (this requires root privileges). Not a good solution.
Instead, since INADDR_BROADCAST is not routed anyway, you can achieve almost the same thing by iterating over each interface, and sending the packet to its broadcast address. For example, assuming that your networks have 255.255.255.0 (/24) masks, the broadcast addresses are 192.168.1.255 and 192.168.2.255. Call sendto() once for each of these addresses and you will have accomplished your goal.
Edit: fixed information regarding to INADDR_BROADCAST, and complementing the answer with information about SO_BINDTODEVICE.
You can't have a single sendto() generate a packet on every interface - in general (fragmentation notwithstanding) it's one packet transmitted for each sendto().
You'll need to transmit the packet once for each interface and either:
use low-level (setsockopt()?) calls to select the outbound interface
send to the specific broadcast address for each known interface
the latter is however not suitable if you're trying to do some sort of discovery mechanism, such that the devices you're expecting to respond aren't actually correctly configured with an IP address in the same subnet as the interface they're connected to.
From Jeremy's solution on UNIX Socket FAQ:
#include <stdio.h>
#ifdef WIN32
# include <windows.h>
# include <winsock.h>
# include <iphlpapi.h>
#else
# include <unistd.h>
# include <stdlib.h>
# include <sys/socket.h>
# include <netdb.h>
# include <netinet/in.h>
# include <net/if.h>
# include <sys/ioctl.h>
#endif
#include <string.h>
#include <sys/stat.h>
typedef unsigned long uint32;
#if defined(__FreeBSD__) || defined(BSD) || defined(__APPLE__) || defined(__linux__)
# define USE_GETIFADDRS 1
# include <ifaddrs.h>
static uint32 SockAddrToUint32(struct sockaddr * a)
{
return ((a)&&(a->sa_family == AF_INET)) ? ntohl(((struct sockaddr_in *)a)->sin_addr.s_addr) : 0;
}
#endif
// convert a numeric IP address into its string representation
static void Inet_NtoA(uint32 addr, char * ipbuf)
{
sprintf(ipbuf, "%li.%li.%li.%li", (addr>>24)&0xFF, (addr>>16)&0xFF, (addr>>8)&0xFF, (addr>>0)&0xFF);
}
// convert a string represenation of an IP address into its numeric equivalent
static uint32 Inet_AtoN(const char * buf)
{
// net_server inexplicably doesn't have this function; so I'll just fake it
uint32 ret = 0;
int shift = 24; // fill out the MSB first
bool startQuad = true;
while((shift >= 0)&&(*buf))
{
if (startQuad)
{
unsigned char quad = (unsigned char) atoi(buf);
ret |= (((uint32)quad) << shift);
shift -= 8;
}
startQuad = (*buf == '.');
buf++;
}
return ret;
}
static void PrintNetworkInterfaceInfos()
{
#if defined(USE_GETIFADDRS)
// BSD-style implementation
struct ifaddrs * ifap;
if (getifaddrs(&ifap) == 0)
{
struct ifaddrs * p = ifap;
while(p)
{
uint32 ifaAddr = SockAddrToUint32(p->ifa_addr);
uint32 maskAddr = SockAddrToUint32(p->ifa_netmask);
uint32 dstAddr = SockAddrToUint32(p->ifa_dstaddr);
if (ifaAddr > 0)
{
char ifaAddrStr[32]; Inet_NtoA(ifaAddr, ifaAddrStr);
char maskAddrStr[32]; Inet_NtoA(maskAddr, maskAddrStr);
char dstAddrStr[32]; Inet_NtoA(dstAddr, dstAddrStr);
printf(" Found interface: name=[%s] desc=[%s] address=[%s] netmask=[%s] broadcastAddr=[%s]\n", p->ifa_name, "unavailable", ifaAddrStr, maskAddrStr, dstAddrStr);
}
p = p->ifa_next;
}
freeifaddrs(ifap);
}
#elif defined(WIN32)
// Windows XP style implementation
// Adapted from example code at http://msdn2.microsoft.com/en-us/library/aa365917.aspx
// Now get Windows' IPv4 addresses table. Once again, we gotta call GetIpAddrTable()
// multiple times in order to deal with potential race conditions properly.
MIB_IPADDRTABLE * ipTable = NULL;
{
ULONG bufLen = 0;
for (int i=0; i<5; i++)
{
DWORD ipRet = GetIpAddrTable(ipTable, &bufLen, false);
if (ipRet == ERROR_INSUFFICIENT_BUFFER)
{
free(ipTable); // in case we had previously allocated it
ipTable = (MIB_IPADDRTABLE *) malloc(bufLen);
}
else if (ipRet == NO_ERROR) break;
else
{
free(ipTable);
ipTable = NULL;
break;
}
}
}
if (ipTable)
{
// Try to get the Adapters-info table, so we can given useful names to the IP
// addresses we are returning. Gotta call GetAdaptersInfo() up to 5 times to handle
// the potential race condition between the size-query call and the get-data call.
// I love a well-designed API :^P
IP_ADAPTER_INFO * pAdapterInfo = NULL;
{
ULONG bufLen = 0;
for (int i=0; i<5; i++)
{
DWORD apRet = GetAdaptersInfo(pAdapterInfo, &bufLen);
if (apRet == ERROR_BUFFER_OVERFLOW)
{
free(pAdapterInfo); // in case we had previously allocated it
pAdapterInfo = (IP_ADAPTER_INFO *) malloc(bufLen);
}
else if (apRet == ERROR_SUCCESS) break;
else
{
free(pAdapterInfo);
pAdapterInfo = NULL;
break;
}
}
}
for (DWORD i=0; i<ipTable->dwNumEntries; i++)
{
const MIB_IPADDRROW & row = ipTable->table[i];
// Now lookup the appropriate adaptor-name in the pAdaptorInfos, if we can find it
const char * name = NULL;
const char * desc = NULL;
if (pAdapterInfo)
{
IP_ADAPTER_INFO * next = pAdapterInfo;
while((next)&&(name==NULL))
{
IP_ADDR_STRING * ipAddr = &next->IpAddressList;
while(ipAddr)
{
if (Inet_AtoN(ipAddr->IpAddress.String) == ntohl(row.dwAddr))
{
name = next->AdapterName;
desc = next->Description;
break;
}
ipAddr = ipAddr->Next;
}
next = next->Next;
}
}
char buf[128];
if (name == NULL)
{
sprintf(buf, "unnamed-%i", i);
name = buf;
}
uint32 ipAddr = ntohl(row.dwAddr);
uint32 netmask = ntohl(row.dwMask);
uint32 baddr = ipAddr & netmask;
if (row.dwBCastAddr) baddr |= ~netmask;
char ifaAddrStr[32]; Inet_NtoA(ipAddr, ifaAddrStr);
char maskAddrStr[32]; Inet_NtoA(netmask, maskAddrStr);
char dstAddrStr[32]; Inet_NtoA(baddr, dstAddrStr);
printf(" Found interface: name=[%s] desc=[%s] address=[%s] netmask=[%s] broadcastAddr=[%s]\n", name, desc?desc:"unavailable", ifaAddrStr, maskAddrStr, dstAddrStr);
}
free(pAdapterInfo);
free(ipTable);
}
#else
// Dunno what we're running on here!
# error "Don't know how to implement PrintNetworkInterfaceInfos() on this OS!"
#endif
}
int main(int, char **)
{
PrintNetworkInterfaceInfos();
return 0;
}

Resources