I am using BSD sockets in Ubuntu 9.10 to send UDP packets in broadcast with the following code:
sock_fd = socket(PF_INET,SOCK_DGRAM,IPPROTO_UDP);
//sock_fd=socket(AF_INET,SOCK_DGRAM,0);
receiver_addr.sin_family = PF_INET;
//does not send with broadcast in ad hoc
receiver_addr.sin_addr.s_addr = htonl(INADDR_BROADCAST);
inet_aton("169.254.255.255",&receiver_addr.sin_addr);
receiver_addr.sin_port = htons(port);
int broadcast = 1;
// this call is what allows broadcast packets to be sent:
if (setsockopt(sock_fd, SOL_SOCKET, SO_BROADCAST, &broadcast,
sizeof broadcast) == -1) {
perror("setsockopt (SO_BROADCAST)");
exit(1);
}
ret=sendto(sock_fd, packet, size, 0,(struct sockaddr*)&receiver_addr,sizeof(receiver_addr));
Note that is not all the code, it is only to have an idea.
The program sends all the data with INADDR_BROADCAST if I am connected to an infrastructure wireless network. However, if my laptop is connected to an ad-hoc network, it is able to receive all the data, but not to send it. I have solved the problem using the 169.254.255.255 broadcast address, but I would like to know what is going on.
Thank you in advance!
Do you have more than one adapter (i.e. wired ethernet, WLAN)?
When you send to the limited broadcast address (INADDR_BROADCAST) your IP stack uses the first adapter and sends the frame. You dont have any control about choosing a specific adapter. The IP stack has done it's job, when it sends to the first adapter found. The frame would be flooded over the whole Internet if this would not be prevented by routers. That's why UDP broadcast are not routable by default. (Exceptions exist as implemented in DHCP relays.)
When you need to send a UDP frame on a specific network, you need the network broadcast address. You did this already, when you send to 169.254.255.255 on a AUTOIP net.
The setsocket(SOL_SOCKET, SO_BROADCAST) just configures the socket to allow sending broadcast at all.
Related
{
in_addr addr;
EFVI_CHECK(::inet_aton("11.231.75.7", &addr));
ef_filter_spec filter_spec;
ef_filter_spec_init(&filter_spec, EF_FILTER_FLAG_NONE);
ef_filter_spec_set_ip4_local(&filter_spec, IPPROTO_TCP, addr.s_addr, htons(21723));
ef_vi_filter_add(&res_.vi, res_.dh, &filter_spec, nullptr);
}
{
// problem is here
ef_filter_spec filter_spec;
ef_filter_spec_init(&filter_spec, EF_FILTER_FLAG_NONE);
ef_filter_spec_set_port_sniff(&filter_spec, 0);
ef_vi_filter_add(&res_.vi, res_.dh, &filter_spec, nullptr);
}
I'm trying to sniff packets sent to a specific netword card at a specific port using Solarflare ef_vi API.
However, when I use ef_filter_spec_set_port_sniff(&filter_spec, 0), it actually sniff all packets received at that netword card. Basically the previous filters are ignored.
When I use ef_filter_spec_set_ip4_local alone, it intercepts the packet instead of sniffing (i.e the packet isn't sent to the kernel).
How can I do this with ef_vi?
what you can do is to interpret all the packets and process only packets matched your port. As far as I know, there is no way to sniff to a specific port using ef_vi APIs.
This is a macOS question. I am trying to setup a UDP socket that receives SSDP messages, i.e. UDP packets, sent to multicast addresses. I want to restrict receiving these packets from a single network interface.
I tried
int fd = socket(AF_INET, SOCK_DGRAM, 0);
char* itf = "en0";
int res = setsockopt(fd, SOL_SOCKET, IP_RECVIF, itf, strlen(itf));
The setsockopt call fails with errno 42 (Protocol not available).
I have also found SO_BINDTODEVICE that can be used for the same purpose, but it seems that this is not available on macOS.
Using bind with port and address also does not work. Then no packets sent to the multicast address are received on that socket.
From the OSX documentation on IP multicast...
A host must become a member of a multicast group before it can receive datagrams sent to the group. To join a multicast group, use the IP_ADD_MEMBERSHIP option...
To receive multicast traffic on a specific interface you need to tell the OS that you want to join that multicast group. Follow these steps (you were almost there)...
Create a datagram socket (done).
Bind to INADDR_ANY with the expected port.
Join the multicast group via setsockopt() with the IP_ADD_MEMBERSHIP option. Here you can pass the IP address of the specific network interface you wish to receive multicast traffic on in the ip_mreq struct.
I have an application on the PC that should get some UDP broadcast messages from a device on the local network.
The device periodically sends UDP broadcast messages to its subnetwork and the application should be able to receive these broadcasts. When the PC and the device are in the same subnetwork there is no problem receiving those messages.
However, when I place the device and the PC in different subnetworks then I can no longer receive the device's broadcasts in the PC application, but I can see them in wireshark.
Scenario 1
So if I have:
the PC at IP 10.0.100.100 with a subnet mask of 255.255.0.0
the device A at IP 10.0.254.83 with a subnet mask of 255.255.255.0
this proof-of-concept PC application:
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <iphlpapi.h>
#include <stdio.h>
#include <assert.h>
#pragma comment(lib, "Ws2_32.lib")
int main()
{
WSADATA wsaData;
int iResult;
iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (iResult != 0) {
printf("WSAStartup failed: %d\n", iResult);
return 1;
}
sockaddr_in si_me, si_other;
int s;
assert((s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) != -1);
int port = 32002;
BOOL broadcast = TRUE;
setsockopt(s, SOL_SOCKET, SO_BROADCAST,
(char*)&broadcast, sizeof(broadcast));
memset(&si_me, 0, sizeof(si_me));
si_me.sin_family = AF_INET;
si_me.sin_port = htons(port);
si_me.sin_addr.s_addr = INADDR_ANY;
assert(bind(s, (sockaddr *)&si_me, sizeof(sockaddr)) != -1);
while (1)
{
char buf[10000];
memset(buf, 0, 10000);
int slen = sizeof(sockaddr);
recvfrom(s, buf, sizeof(buf)-1, 0, (sockaddr *)&si_other, &slen);
char *ip = inet_ntoa(si_other.sin_addr);
printf("%s: %s\n", ip, buf);
}
}
Then I don't receive the broadcast messages from the device.
Scenario 2
However if I have a device B at IP 10.0.255.222 with subnet mask of 255.255.0.0 I can receive the messages, even though the PC is still in another subnetwork.
Scenario 3
If I move the PC at 10.0.254.100 with subnet mask 255.255.255.0 then I can communicate with the device A, but then I cannot see the messages from the device B at 10.0.255.222.
The thing that confuses me more is that in both cases Wireshark can capture the packets.
Why can't my application see the packets from device A and why wireshark can (in the first scenario)? What can I do similar to wireshark so I can see those packets?
What's the explanation behind scenario 2 and 3?
Since in scenario 2 the device B is clearly in another subnetwork, but the loss of communication happens only in scenario 3
What should I read to get a better understanding of these issues?
PS: I don't think the problem comes from the UDP's unreliability.
PPS: I did try to disable "Capture packets in promiscuous mode", the result is the same, I can see the packets from the device A in Wireshark
One thing is - Wireshark uses promiscuous mode - so it can read anything that comes on the switch port. This might include your own broadcasts, some other broadcasts and even some uni/multicasts which are not meant for you - provided the packet comes to that switch port. This is not the case with your program. Your program is trying to receive datagrams for the network broadast address only. Now the explanation below would help.
I think you are confusing network Addresses and broadcast domains. Here is what is happening in your case
Scenario 1.
PC - 10.0.100.100 netmask 255.255.0.0 so the broadcast address is 10.0.255.255, (In general anything that is 0 in netmask - if that is set to all 1s becomes a broadcast address. )
So anything sent to this broadcast address will be received by your App.
Device A 10.0.254.83 netmask 255.255.255.0. So the broadcast address is 10.0.254.255. Note it is different from the broadcast address of PC and hence - you cannot receive them.
So don't get confused by outwardly looking identical network addresses.
Scenario 2
Device B - 10.0.255.222 netmask 255.255.0.0. So The broadcast address is 10.0.255.255. This is same as your PC's address in scenario 1 above and hence can receive the datagrams (without being in promiscuous mode). (The 255.222 part might look like a different network, but it is not because the netmask is 16 byte 255.255.0.0.
Scenario 3
PC : 10.0.254.100 netmask 255.255.255.0 so the broadcast address for the network is 10.0.254.255 - which is same as Device A's broadcast address (10.0.254.255) in Scenario 1, but not same as broadcast address of Device B (10.0.255.255) in Scenario 2 and hence the expected outcome.
In your scenario - the Device A's network is a subnet of Device B and PC's network and broadcasts are restricted to subnets only. When you move PC into the subnet you receive the broadcast of the Subnet (Senario 3).
Edit:
Explanation - why you can see subnetwork broadcasts in wireshark and why not in your application is - What you see in wireshark is whatever is 'received by your network card'. Since all broadcasts do have a destination mac of ff:ff:ff:ff:ff your card is going to receive it, but your OS stack is going to drop them as the network level address is not the network level broadcast. And Layer 2 broadcasts will be received even without promiscuous mode.
Hope that helps.
Wireshark puts the network adapter into "promiscuous mode" where it reports all packets it sees on the network. Normally it reports only the packets destined to the host. It sounds like in your case the difference in network mask means the device will use a different broadcast address, and the packets are dropped.
Wireshark installs a special network driver on Windows to get access to all traffic https://wiki.wireshark.org/WinPcap
Whenever you run wireshark .it automatically put your device (interface) in
"promiscuous mode" .so it will capture all the packets which is in the air ,
without bothering the subnet mask.
whereas your PC will capture the UDP frame which is multicasted on the
same subnet .
I'm 99% new to sockets and any sort of network programming, so please bear with me.
I am aiming to connect to a port (2111 in this case) on my local machine (192.168.0.1). From there, I'm planning on sending and receiving basic information, but that's for another day.
I've currently tried this:
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
int main(int argc, char **argv)
{
int sd;
int port;
int start;
int end;
int rval;
struct hostent *hostaddr;
struct sockaddr_in servaddr;
start = 2111;
end = 2112;
for(port = start; port <= end; port++)
{
sd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
if(sd == -1)
{
perror("Socket()\n");
return (errno);
}
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(port);
hostaddr = gethostbyname("192.168.0.1");
memcpy(&servaddr.sin_addr, hostaddr->h_addr, hostaddr->h_length);
rval = connect(sd, (struct sockaddr *)&servaddr, sizeof(servaddr));
if(rval == -1)
{
printf("Port %d is closed\n", port);
close(sd);
}
else printf("Port %d is open\n", port);
close(sd);
}
return 0;
}
However, my connect() call hangs for about 90 seconds, then returns -1.
The device is directly connected to my Mac Mini's ethernet port and the manufacturer has confirmed that the port is 2111 or 2112.
What am I doing wrong? Also, can it be in the ELI5 (explain like I'm 5) format? I'm much better off with examples.
When you call connect() to connect to a host, your computer sends a SYN packet to begin the three-way handshake of the TCP connection. From here, there are 3 possible scenarios:
If the peer is listening on that port, it responds with a SYN+ACK packet, your computer responds with a final ACK, and the connection is established—connect() returns successfully.
If the peer is not listening on that port, it responds with an ICMP packet with a type and code indicating that the port is closed, which causes your connect() call to fail almost immediately with the error ECONNREFUSED (connection refused). Under normal circumstances, this takes 1 network round-trip time (RTT) to happen, which is typically tens or hundreds if milliseconds.
If your computer never receives either an appropriate SYN+ACK TCP packet or connection refused ICMP packet, it assumes that its original SYN packet got dropped by the network somewhere and will try to resend the SYN packet several times until it gets one of those packets back or it hits an OS-dependent timeout, at which point the connect() call fails with ETIMEDOUT. This is typically 1–2 minutes, depending on the OS and its TCP settings.
You're clearly hitting case #3. This can be caused by a few different issues:
Your original SYN packets were getting lost in the network, possibly due to a faulty link, overloaded router, or firewall
The peer's SYN+ACK or ICMP response packets were getting lost in the network, possibly due to a faulty link, overloaded router, or firewall
The destination address may be unroutable/unreachable
The peer may be failing to properly respond at all with a SYN+ACK or ICMP packet
If you're directly connecting to the device over ethernet, than that rules out #1 and #2. #4 is possible, but I think #3 is the most likely explanation.
A brief aside on packet routing
Your computer has multiple network interfaces—ethernet (sometimes multiple ethernet interfaces), Wi-Fi, the loopback device, VPN tunnels, etc. Whenever you create a socket, it has to be bound to one or more particular network interfaces in order for the OS to know which NIC to actually send the packet through. For listen sockets for servers, you typically bind to all network interfaces (to listen for connections on all of them), but you can also bind to a particular network interface to only listen on that one.
For client sockets, when you connect them to other peers, you don't normally bind them to a particular interface. By default, your computer uses its internal routing tables along with the destination IP address to determine which network interface to use. For example, if you have a gateway machine with two NICs, one of which is connected to the public internet with IP 54.x.y.z and hte other of which is connected to an internal, private network with IP 192.168.1.1, then that machine will in all likelihood have routing tables that say "for packets destined to 192.168.0.0/16, use NIC 2, for all other packets, use NIC 1". If you want to bypass the routing tables, you can bind the socket to the network interface you want by calling bind() on the socket before the call to connect().
Putting it all together
So, what does that all mean for you?
First, make sure that 192.168.0.1 is in fact the correct destination address you should be connecting to. How is that address determined? Is your computer acting as a DHCP server to assign that address to the other host? Is that host using a static IP configuration?
Next, make sure that your routing tables are correct. If the other machine is assigning itself a static IP, chances are that your Mac isn't aware of how to route to that destination and is probably trying to route through the wrong interface. You can manually adjust the routes on Mac OS X with the route(8) utility, but these get reset every reboot; this blog post shows an example of using a startup item to automate adding the new route on startup. You'll want to use the IP address associated with the ethernet interface connected to the target host.
Alternatively, instead of using routing tables, you could call bind() on your socket before connect() to bind to the local address of the interface you want to use, but this won't work for other programs unless they also provide that functionality. For example, the curl(1) utility lets you pass the --interface <name> command line flag to direct it to bind to a particular interface.
Basically, connect() is failing (check errno for why).
You might consider a implementing some kind of time-out for the connect. To do this, set the socket to non-blocking mode. Then call connect(), and then use select() to wait for a response with a timeout.
SPOILER Example of ConnectWithTimeout() for Linux
I have the following issue here: I want to write a server streaming data on a UDP socket on a specific port, and clients should be able to connect to it and receive the data that is being sent out without too much hassle: they just connect, and from the moment they start they should get data using recvfrom from the server.
I have some problems with setting up the network related parts. So, here is a rough piece of code that I try to make work:
int udpSock = socket(AF_INET, SOCK_DGRAM, 0);
if(udpSock == -1)
{
perror("Could not create audio output socket");
exit(1);
}
struct sockaddr_in *sin = (struct sockaddr_in*)&gOutgoingAddr;
sin->sin_port = htons(40200);
if(bind(udpSock, (const sockaddr*)sin, sizeof(struct sockaddr_in)) == -1)
{
perror("Cannot bind audio socket");
exit(1);
}
int buffer_size = 0;
char* data = get_next_buffer(&buffer_size);
while(buffer_size > 0)
{
if(sendto(udpSock, (const void*)(data), buffer_size, 0, NULL, 0) == -1)
{
perror("sendto failure");
}
data = get_next_buffer(&buffer_size);
}
Do not worry about the gOutgoingAddr variable, it is obtained correctly using getifaddrs, it is valid. I am troubled regarding the parametrization of the sendto method, becasue right now the output of the application is:
sendto failure: Destination address required
That's true, because I don't have a destination address, since all the examples I have found till now show when the server gets a client connection, and there is the address. But since I don't have a client yet connected, I'd still want to stream out.
I appreciate all help, I have no idea what Ishould put for the parameters of sendto:
The gOutgoingAddress which is the address where I create the socket? I have tried this, but if I use the tcpdump linux command on the specified port, I get nothing.
Should I create a multicast socket? This somehow makes no sense...
Something else?
Thanks,
frc
You cannot stream out to "nowhere". Streaming data via UDP is not multicast. That means if you have 100 clients connected, you must send exactly the same data 100-times, once to each of the clients that shall receive it. Multicast was not really part of the initial IPv4 design. It has been added later on and is not widely supported. This is contrary to IPv6, where multicast has been part of the initial design. The only thing you could do it broadcast the traffic within your local network. This will only work if all clients are in your local network segment. To broadcast your server would simply send the data to 255.255.255.255 and to a fixed UDP port. All clients then have to listen on that specific port and will receive the data. Please note, that on most systems you need special permissions for broadcasting (e.g. it is not common that only programs running with root privileges are allowed to broadcast traffic, as broadcasts pollute your network, since all broadcast packets are sent to all clients in the network, whether they care for them or not). Without broadcasts, you have only unicast and unicast means one sender, one receiver. For one sender multiple receiver, you must send out the same data multiple times to multiple addresses.
What is audioUdpSock by the way?
Shoudn't you be using udpSock instead?
Do a recvfrom in your server, and have the client send a message (with whatever content you want, this is just a way to establish the connection, a greeting). Then the server will have the client address from the recvfrom, and can send packet to it.
As UDP socket are connection-less (there is no need for accept and connect when using UDP socket), you need to have another way to inform the server of the existence of the client (and the client need to have an out-of-bound way to know the server address, generally, the user give it, or it is hard-coded).
If you can have multiple clients, then you'll have to use select, poll, ... on the socket to know when it is safe to call recvfrom without blocking (or you could configure your socket to be non-blocking).
Edit: I highly recommend Beej's Guide to Network Programming to everyone, and for your question, you can directly go to the sample usage of Datagram Socket.