I am trying to modify a multicast listener / sender example to bind the UDP / multicast socket to a specific interface and not using the INADDR_ANY macro.
I possess the IPv4 address of the interface.
I tried the following, but the socket does not receive any UDP (unicast, broadcast, multicast) packets.
struct sockaddr_in addr;
int fd, nbytes;
socklen_t addrlen;
struct ip_mreq mreq;
// my_ipv4Addr equals current IP as String, e.g. "89.89.89.89"
// create what looks like an ordinary UDP socket */
if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
perror("socket");
exit(1);
}
// set up addresses
bzero(&addr, sizeof(addr));
addr.sin_family = AF_INET;
// [-] addr.sin_addr.s_addr = htonl(INADDR_ANY);
addr.sin_addr.s_addr = inet_addr(my_ipv4Addr);
addr.sin_port = htons(port);
// bind socket
if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
perror("bind");
exit(1);
}
// use setsockopt() to request that the kernel join a multicast group
mreq.imr_multiaddr.s_addr = inet_addr(group);
// [-] mreq.imr_interface.s_addr = htonl(INADDR_ANY);
mreq.imr_interface.s_addr = inet_addr(my_ipv4Addr);
if (setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq))< 0) {
perror("setsockopt");
exit(1);
}
Edit:
Let me explain the purpose of my program. I am writing a little tool, which will check, if a network supports broadcast/multicast. Therefore I own a system with two interfaces and send via Interface1 a multicast Packet and try to receive it with Interface2. But: The packet shall go through the network, not the loopack device.
The idea is to block multicast-loopback on thread1/interface1 with:
u_char loop = 0;
setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, &loop, sizeof(loop));
And to listen on thread2/interface 2 interface-specific. Tcpdump shows, that the packets are arriving, but are dropped with my config above.
with
addr.sin_addr.s_addr=inet_addr(my_ipv4Addr);
bind(sockfd,(SA*)&addr,sizeof(addr));
you can only send out packets to the multicast group,
but you can't recv any packets, even those send out from `my_ipv4Addr'.
so addr.sin_addr.s_addr must be htonl(INADDR_ANY).
with
mreq.imr_interface.s_addr=inet_addr(my_ipv4Addr);
you can recv all packets from the multicast group,
but it send out packets with the default interface (maybe eth0),
not the one you specified (like eth1).
So this is no effect.
with
setsockopt(sockfd,SOL_SOCKET,SO_BINDTODEVICE,ETH1,strlen(ETH1));
you can send out packets through the interface ETH1,
but you can only recv packets send out from the ip associated with ETH1,
you can't recv any packets from other clients.
with
mreq.imr_interface.s_addr=inet_addr(my_ipv4Addr);
setsockopt(sockfd,IPPROTO_IP,IP_MULTICAST_IF,&mreq.imr_interface,sizeof(struct in_addr);
you can send out packets through the interface associated with my_ipv4addr,
also you can recv any packets from any clients in the multicast group.
bzero(&addr, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl(INADDR_ANY);
//addr.sin_addr.s_addr = inet_addr(my_ipv4Addr);
addr.sin_port = htons(port);
mreq.imr_multiaddr.s_addr = inet_addr(group);
// [-] mreq.imr_interface.s_addr = htonl(INADDR_ANY);
mreq.imr_interface.s_addr = inet_addr(my_ipv4Addr);
You just need to edit your code like mine.
When binding a socket for receiving multicast traffic, if you bind to a local address, this prevents multicast packets from being received on non-Windows systems.
I first discovered this when I released version 3.6 of UFTP with the feature of binding to a specific address. Windows handles it just fine, but on Linux systems the multicast packets weren't received by the app. I had to remove the feature in the next release.
Note that you are allowed to bind directly to the multicast address:
addr.sin_addr.s_addr = inet_addr(group);
In this case, you'll receive traffic only for that multicast address on that socket. You still need to join the multicast group on a specific interface to receive however.
If you plan on receiving data from more than one multicast address on the same socket, then you need to bind to INADDR_ANY.
Either you simplified your code for the sake of understanding or i have missed something,
This is the struct
struct ip_mreqn {
struct in_addr imr_multiaddr; /* IP multicast group
address */
struct in_addr imr_address; /* IP address of local
interface */
int imr_ifindex; /* interface index */
};
ip man page - IP_ADD_MEMBERSHIP
But you are referring
mreq.imr_interface.s_addr = inet_addr(my_ipv4Addr);
What is imr_interface? Does it compile?
In case you just wrote the name above for better readibility, have you tried filling the interface index i.e. imr_ifindex to the specific interface you want to attach to.
My guess is, if you leave the imrr_address and assign the interface index only, it should bind to that interface and receive packets. See if that helps.
Related
Below is basic code to create a multicast socket on Linux.
I have multiple processes running the below code on one machine, each listening to a different multicast channel. Only the relevant data should be processed by each process.
Two stages in the code require an address, or INADDR_ANY.
Given I will have multiple separate multicast channels running, I'm unsure when I should use INADDR_ANY or specify the address. I don't want a process receiving the wrong multicast data.
// Create socket
int sock = socket(AF_INET, SOCK_DGRAM, 0);
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes));
// Create multicast group
sockaddr_in mcast_group
memset(&mcast_group, 0, sizeof(mcast_group));
mcast_group.sin_family = AF_INET;
mcast_group.sin_port = htons(mcastPort);
mcast_group.sin_addr.s_addr = INADDR_ANY; // INADDR_ANY or specific address?
// Bind socket to multicast group
bind(sock, (struct sockaddr*)&mcast_group, sizeof(mcast_group));
// Tell Kernel to join multicast group
struct ip_mreq mreq;
mreq.imr_multiaddr.s_addr = inet_addr(group.c_str());
mreq.imr_interface.s_addr = inet_addr(interface.c_str()); // INADDR_ANY or specific address?
setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
If each socket is listening only for multicast packets and not unicast, and only for a single multicast address, you can bind directly to the multicast address:
sockaddr_in mcast_group;
memset(&mcast_group, 0, sizeof(mcast_group));
mcast_group.sin_family = AF_INET;
mcast_group.sin_port = htons(mcastPort);
mcast_group.sin_addr.s_addr = inet_addr(group.c_str()); // bind to multicast group
bind(sock, (struct sockaddr*)&mcast_group, sizeof(mcast_group));
Note that you'll still have to join the multicast group.
bind to INADDR_ANY will receive unicast as well as multicast packets. bind to the multicast address will only receive the multicast packets. THIS IS the different between Linux socket and Windows socket programming for multicast receiving.
I have an assignment to make a Routing Information Protocol sniffer and a program in C/C++ capable of sending custom RIP entry using the information gathered from the sniffer.
I'm running both in a school provided Ubuntu Virtualbox image, which is connected via "Internal Network" option to a BSD image running a routing daemon generating RIP traffic.
I got to the part where I need to send the custom packet to the "router" but I hit a wall when trying to send it from port 520.
From the RFC 1058 describing the RIP protocol I gathered that in order for a router to acknowledge a new route, the RIP message has to come from and to the port 520.
I can send my packet to port 520 of the router just fine, but the source port is always a random port assigned by system after my binding fails with an errno message "Cannot assign requested address".
The packet itself looks just fine in WireShark, with the exception of the source port which is for example 60818.
I am doing the following in my response program:
#define ROUTERADDR "10.0.0.1"
int fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
struct sockaddr_in router;
router.sin_family = AF_INET;
router.sin_port = htons(520);
router.sin_addr.s_addr = inet_addr(ROUTERADDR);
bind (fd, (struct sockaddr*)&router, sizeof(router));
connect (fd, (struct sockaddr*)&router, sizeof(router));
send(fd, &payload, sizeof(payload), 0);
close(fd);
Binding to a port < 1024 requires root permission, unless you add your executable to CAP_NET_BIND_SERVICE capability as below.
setcap 'cap_net_bind_service=+ep' /path/to/executable
There is another issue in your code, where you are trying to bind to router's address. You need to bind to the specific local address or INADDR_ANY, as shown below.
struct sockaddr_in local, router;
local.sin_family = AF_INET;
local.sin_port = htons(520);
local.sin_addr.s_addr = htonl(INADDR_ANY);
bind (fd, (struct sockaddr*)&local, sizeof(local));
router.sin_family = AF_INET;
router.sin_port = htons(520);
router.sin_addr.s_addr = inet_addr(ROUTERADDR);
connect (fd, (struct sockaddr*)&router, sizeof(router));
You should check the return value of your system calls, so you know when things don't work, as in your case the bind() call likely fails.
In your case bind() would fail because you try to bind to a port on the remote address you're sending to, which you cannot do.
bind() specifies the local endpoint you want to use, thus you should specify one of the local IP addresses you want to send from.
Or you can bind the socket to any local address, using INADDR_ANY. so you might want to create another struct sockaddr_in and do it like this:
struct sockaddr_in my_addr;
my_addr.sin_family = AF_INET;
my_addr.sin_port = htons(520);
my_addr.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(fd, (struct sockaddr*)&my_addr, sizeof(my_addr)) != 0) {
perror("bind() failed");
}
Rewritten to try and be clear on what I need.
My goal is to duplicate the function of a device made by Digital Yacht in an embedded Intel Edison processor running C and Linux. The device sends via UDP to phone apps such as iRegatta and others. To set up the app, only the port number is entered. No ip address is entered in UDP mode on the app. I thought this was trivial but the experts here so far have said it is impossible so it must not be trivial. Perhaps that is why with all my hours of reading I cannot find an example. I am being voted down because, I am told, that what I am trying to do it impossible but it is not as it is done. I don't know how it is done, which is why I came to experts here.
I want to send nmea messages that might look like this:
$GPGGA,123519,4807.038,N,01131.000,E,1,08,0.9,545.4,M,46.9,M,,*47
and I want any number of random Android phones to be able to receive them with the appropriate app. There are many apps that can be set up to receive UDP data where you just specify a port number. There is no ip address involved in the setup of the apps. Also, I do not wish to receive anything from the Android phones. This is one way and no ability to re-transmit so if a message does not get there, it has another chance next time. Everything is updated once a second.
I tried the following and I do not get data in the app. From the comments, I must need to add some kind of router function in my Linux machine.
void init_udp(){
return;
sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (sock < 0){
printf("ER UDP Socket error\n");
}
else printf("UP Socket %d OK\n",sock);
}
void write_udp(char *buf){
return;
// nmea data is in buff
if (sock >= 0){
int on = 1;
setsockopt( sock, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on) );
struct sockaddr_in address = {0};
address.sin_family = AF_INET;
address.sin_addr.s_addr = inet_addr( "255.255.255.255" ); //
address.sin_port = htons( 3000 );
if ( sendto( sock, buf, strlen(buf), 0, (struct sockaddr*)&address, sizeof(address) ) < 0) printf("ER UDP send error\n");
else {
printf("UP %s\n",buf);
}
}
}
I am not really sure what I need to do.
What you want to do is send a UDP packet to a broadcast IP address. This will send to thing in the subnet.
eg 10.255.255.255 is the broadcast address for the 10.x.x.x subnet. You can also use the global 255.255.255.255 which should also send to your subnet and no router is going to pass that on to another one these days.
Also you need to make your socket able to send broadcast messages. In particular you need the option SO_BROADCAST set. The following is specifically Windows because of the BOOL. Its presumably an int for most platforms.
BOOL on = TRUE;
setsockopt( sock, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on) );
Also you can't use send() for a UDP socket unless its "connected", so you should use sendto() and use the broadcast address.
To specify an address and port, use need to create a socket address.
struct sockaddr_in address = {0};
address.sin_family = AF_INET;
address.sin_add.s_addr = inet_addr( "255.255.255.255" );
address.sin_port = htons( 3000 );
sendto( sock, buff, strlen(buff), 0, (struct sockaddr*)&address, sizeof(address) );
Note the use of the typecast. This is because sendto takes a sockaddr which is a generic address type, and here we are using an IPV4 address specifically. The sockaddr_in maps onto the sockaddr
I have a server implementation where I need 2 separate sockets - 1 IPv4 socket socket listening on a particular IPv4 address and server port X, and an IPv6 socket listening on a particular IPv6 address and same server port X. The IPv4 and IPv6 addresses are on the same interface.
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = htonl(v4addr);
sin.sin_port = htons(tcp_port);
I am using evconnlistener_new_bind to create ipv4 socket and bind to it.
For IPv6 listener, the code is as below.
memset(&sin6, 0, sizeof(sin6));
sin6.sin6_family = AF_INET6;
memcpy(sin6.sin6_addr.s6_addr, v6addr_bytes, IPV6_ADDR_LEN);
sin6.sin6_port = htons(tcp_port);
fd = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
evutil_make_socket_nonblocking(fd)
setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (void*)&on, sizeof(on))
setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (void*)&on, sizeof(on))
evutil_make_listen_socket_reuseable(fd) /* Libevent call to set SO_REUSEADDR */
evutil_make_socket_nonblocking(fd) /* Libevent call to set fd non-blocking */
bind(fd, (const struct sockaddr *)&sin6, sizeof(sin6))
As I bind my fd to the particular ipv6 address, I see a bind failure intermittently.
bind v6 failed sin6 3ffe::a00:513 - errno 99 - Cannot assign requested address
I tried to gdb in, but every time I gdb in, the bind succeeds.
I am not sure why I am seeing this problem. Can someone please help?
By default, after a socket is bound to a TCP port, the port remains reserved for one minute when the socket is closed — this is called the TCP TIME_WAIT state. TIME_WAIT avoids some race conditions that could cause data corruption, but it is usually safe to ignore TIME_WAIT on the server side.
This is done by setting the SO_REUSEADDR socket option:
int one = 1;
rc = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one))
I have a C code snippet that listens on UDP socket for incomming messages (and it works fine):
uint32_t udp_port = 101010; // example port
int sock_udp = socket(AF_INET, SOCK_DGRAM, 0);
server_address.sin_family = AF_INET;
server_address.sin_addr.s_addr = htonl(INADDR_ANY);
server_address.sin_port = htons(udp_port);
bind(sock_udp, (struct sockaddr*) &server_address, (socklen_t) sizeof(server_address));
char buffer[20];
struct sockaddr_in sender_address;
socklen_t sender_len = (socklen_t) sizeof(struct sockaddr_in);
ssize_t rcv_len = recvfrom(sock_udp, buffer, sizeof(buffer), 0, (struct sockaddr * ) &sender_address, &sender_len);
After it I have information on the sender in sender_address structure and I can check addres, port etc. My question is: can I use recv, recvfrom or other similar function to listen for datagrams coming from a certain host? In other words, is it possible to drop datagrams from other sources without reading them?
You can "filter" and receive datagrams from a specified single source if you connect(2) the datagram socket.
If the socket sockfd is of type SOCK_DGRAM then addr is the address to
which datagrams are sent by default, and the only address from which
datagrams are received.
The standard phrases it a little different:
For SOCK_DGRAM sockets, the peer address identifies where all
datagrams are sent on subsequent send() functions, and limits the
remote sender for subsequent recv() functions