This code works perfectly fine on Ubuntu 16.04 and prints correct value (ETHERTYPE_IP) when I toss around UDP bytes via loopback interface:
#include <pcap.h>
#include <iostream>
#include <net/ethernet.h>
int main(int argc,char **argv)
{
char errbuf[PCAP_ERRBUF_SIZE];
auto pcap = pcap_open_live("lo0", BUFSIZ, 0, 1000, errbuf);
pcap_loop(pcap,0, [] (u_char *self, const struct pcap_pkthdr *header,
const u_char *packet) {
auto eth = (struct ether_header *) packet;
auto eth_type = ntohs(eth->ether_type);
std::cout << "eth_type: " << std::hex << eth_type << std::endl;
}, nullptr);
return 0;
}
netcat:
➜ ~ nc -uv -l 54321
Listening on [0.0.0.0] (family 0, port 54321)
➜ ~ nc -4u localhost 54321
hello
Program output:
➜ ~ sudo ./a.out
eth_type: 800
However on OS X 10.11.5 it prints eth_type: 4011. Interesting that it works fine with en1 adapter.
Why there is such a difference between loopback and non-loopback adapters and what is the correct way to capture packets on both?
Update:
tcpdump also works:
➜ ~ sudo tcpdump -i lo0
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on lo0, link-type NULL (BSD loopback), capture size 262144 bytes
15:09:00.160664 IP localhost.54321 > localhost.63543: UDP, length 4
As the link type is not ethernet, the header does not contain suitable data for ether_header.
Add this code after fetching the handle with pcap_open_live to see the link-layer header type:
if (pcap_datalink(pcap) != DLT_EN10MB) {
fprintf(stderr, "Device doesn't provide Ethernet headers - link type was %d\n", pcap_datalink(pcap));
return 1;
}
Running this indicates that the linktype value for lo0 is 0, DLT_NULL. The documentation states that this means "BSD loopback encapsulation; the link layer header is a 4-byte field, in host byte order, containing a PF_ value from socket.h for the network-layer protocol of the packet."
Indeed, when I look at the the first 4 bytes of the ether_dhost field I see the value 2, corresponding to PF_INET. In the end, this doesn't help you much if you are trying to distinguish UDP packets.
You can find more documentation here: http://www.tcpdump.org/linktypes.html
Related
I have a Raspberry Pi 4 running a C++ program where it receives and sends data via UDP. The RPi is setup as a UDP server.
The code for UDP.hpp is:
#pragma once
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <string>
using namespace std;
/////GLOBAL CONSTANTS/////
const int c_PORT = 8080;
class UDP
{
private:
int fdSocketUDP_; //File descriptor for UDP socket
int ClientAddressLength_; //Length of client address
struct sockaddr_in ServerAddress_; //Struct handling internet address for server
struct sockaddr_in ClientAddress_; //Struct handling internet address for client
public:
UDP(); //Initialize and bind socket
~UDP(); //Close socket
string readUDP(const int readSize); //Read via UDP protocol
void writeUDP(string message); //Write via UDP protocol
};
The code for UDP.cpp is:
#include "udp.hpp"
UDP::UDP()
{
if ((fdSocketUDP_ = socket(AF_INET, SOCK_DGRAM, 0)) < 0) //Create UDP socket
{
perror("Error - socket creation - udp.cpp");
exit(EXIT_FAILURE);
}
memset(&ServerAddress_, 0, sizeof(ServerAddress_)); //Sets ServerAddress_ to 0
memset(&ClientAddress_, 0, sizeof(ClientAddress_)); //Sets ClientAddress_ to 0
ServerAddress_.sin_family = AF_INET; //Address family, must be AF_INET = IPv4
ServerAddress_.sin_port = htons(c_PORT); //PORT number, convert PORT number to network byte order using htons()
ServerAddress_.sin_addr.s_addr = INADDR_ANY; //IP-Address of host (server IP), INADDR_ANY gets this IP Address
if (bind(fdSocketUDP_, (const struct sockaddr *)&ServerAddress_, sizeof(ServerAddress_)) < 0) //Bind the socket to ServerAddress_
{
perror("Error - socket bind - udp.cpp");
exit(EXIT_FAILURE);
}
}
UDP::~UDP()
{
close(fdSocketUDP_); //Close socket
}
string UDP::readUDP(const int readSize)
{
char readMsg[readSize] = {0}; //Read buffer
ClientAddressLength_ = sizeof(ClientAddress_);
if ((recvfrom(fdSocketUDP_, readMsg, readSize, 0, (struct sockaddr *)&ClientAddress_, (socklen_t *)&ClientAddressLength_)) < 0) //Receive data via UDP protocol
{
perror("Error - recvfrom - udp.cpp");
exit(EXIT_FAILURE);
}
string str(readMsg); //Convert char array to string
str = str.substr(0, readSize); //Make sure the string is the length of readsize
return str;
}
void UDP::writeUDP(string message)
{
//Make char array
int writeSize = message.size();
char writeMsg[writeSize + 1] = {'\0'};
//Convert string message to char array
for (int i = 0; i < writeSize; i++)
{
writeMsg[i] = message[i];
}
if ((sendto(fdSocketUDP_, writeMsg, writeSize, 0, (const struct sockaddr *)&ClientAddress_, (socklen_t)ClientAddressLength_)) < 0) //Send data via UDP protocol
{
perror("Error - sendto - udp.cpp");
exit(EXIT_FAILURE);
}
}
I then have a windows 10 laptop, running a Labview program that also receives and sends data via UDP. The laptop is setup as a UDP client. Below are som examples of the UDP setup in Labview.
Image 1 (Open UDP connection):
Image 2 (Close UDP connection):
Image 3 (Write and read UDP in Labview):
Above, the Labview program on the laptop sends 3 ("103") + 37 (not shown) bytes of data to the RPi, and then receives 16 bytes of data from the RPi.
The laptop and RPi are connected via a LAN-cable on a local network. The RPi uses IP-address 10.10.10.10 and port 8080, and the laptop uses IP-address 10.10.10.1 and port 1000.
Below are a Wireshark measurement, that measures the time between the different send and receive commands between the RPi and laptop.
Image 4 (wireshark measurement):
The "Len=3" is used by the RPi to determine what function to run in the C++ code. The "Len=52" and "Len=37" is data sent from the laptop (Labview) to the RPi (C++ code). The "Len=16" is data sent from the RPi to the laptop.
The laptop first sends 3+52 bytes of data to the RPi (client sends data to server). The laptop then sends 3+37 bytes of data to the RPi (client sends data to server). The RPi then sends 16 bytes of data back to the laptop (server sends data to client)... and so on.
One command (3+52 bytes or 3+37+16 bytes) takes about ~8ms to finish, with ~2ms latency (on average) between each command. As you can see, the data sizes between the RPi and laptop are "relatively" small (3/37/52 bytes).
Now my problem: Sometimes there is a delay of ~20ms between the commands (10 times longer than the average of ~2ms), and I don't know why... (this is shown with the red dots on image 4). This delay often comes after the RPi (UDP server) sends data to the laptop (UDP client - the 16 bytes of data), but it can happen at different places, as shown on image 4 (after the laptop sends 52 bytes to the RPi). I think it has something to do with the UDP, maybe the setup, maybe it has something to do with ARP, but I don't know. I have tried overclocking the RPi, tweaking the priority of the C++ program on the RPi, tweaking the C++ code, but that doesn't seem to be the bottleneck.
It's like the UDP connection between the laptop and RPi is "lost" or "paused" sometimes and it then takes some time for the connection to come back on track.
I found a solution to my problem. To solve the long delay, i had to lower the UDP read buffer, since i'm only sending small packages via UDP.
To do this, i formatted the sysctl.conf file on the RPi, located in the /etc folder.
I added the lines:
net.core.rmem_default = 4096
net.core.rmem_max = 4096
I have a short program to send UDP data to a local socket like so.
const char *i = "localhost";
const char *p = "8980";
struct addrinfo h;
struct addrinfo *res = 0;
memset(&hints,0,sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_DGRAM;
hints.ai_protocol = 0;
hints.ai_flags = AI_ADDRCONFIG;
if (getaddrinfo(i, p, &hints, &res) != 0)
{
printf("ERROR: getaddinfo\n");
}
int fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
if (fd == -1)
{
printf("ERROR: socket\n");
freeaddrinfo(res);
}
if (sendto(fd, "hello", 5, 0, res->ai_addr, res->ai_addrlen) == -1)
{
printf("ERROR: Sending\n");
}
freeaddrinfo(res);
close(fd);
I have launched ncat in a different terminal window with ncat -ul localhost 8980 and I can see "hello" on it when I subsequently launch the above program. I can also perform repeated calls to sendto and see hello multiple times. The program terminates but I cannot see an additional "hello" message on ncat when I relaunch the sending program. Why is this?
I've also tried not calling close at the end of the program as well.
The issue is that every time you run your program it will send its data from a different port, and thus be seen as a different network endpoint. When ncat
first receives a packet, it will bind to the remote endpoint and stop listening for packets coming from any other endpoint.
You can work around this with ncat by using the -k option. Sadly, ncat's -k option can only be used with the -e or -c options when doing UDP. You can make it work with:
ncat -ulkc "cat > $(tty)" localhost 8980
It will still bind to each remote endpoint it gets anything from though, so there's a limit to the number of times it will work (default 100, configurable via the -m option).
It's a bit easier if you use nc instead. It's -k option works normally with -u:
nc -ulk localhost 8980
That will prevent nc from binding to the remote endpoint at all, so it doesn't have the same limit as ncat.
I have written a device discovery program that can run in client or server mode. In client mode it sends a UDP broadcast packet to 255.255.255.255 on port 30000 and then listens for responses on port 30001. In server mode it listens for a UDP broadcast on port 30000 and sends a UDP broadcast packet to 255.255.255.255 on port 30001 in response.
When I run this program on 2 devices with IP addresses 192.168.10.61 and 192.168.10.62 it all works perfectly. The whole point of this program is to allow devices with unknown IP addresses to discover one another so long as they are connected to the same physical network. So to test that, I changed the IP address of the first device to something random like 12.34.56.42/255.255.240. And now it stops working.
Using tcpdump on the 192.168.10.62 machine I can see that the UDP packet from the 12.134.56.42 machine was received:
# tcpdump -i eth0 port 30000 -c 1 -v
tcpdump: listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes
17:38:02.552427 IP (tos 0x0, ttl 64, id 18835, offset 0, flags [DF], proto UDP (17), length 49)
12.34.56.42.56815 > 255.255.255.255.30000: UDP, length 21
1 packet captured
6 packets received by filter
0 packets dropped by kernel
but my discovery program no longer receives it. This is the code I am using to receive the UDP broadcast packets:
int WaitForPacket(uint16_t portNum, vector<char>& udpBuf, udp::endpoint& remoteEndpoint, const chrono::milliseconds timeout)
{
io_service ioService;
udp::socket socket(ioService, udp::endpoint(boost::asio::ip::address_v4::any(), portNum));
socket.set_option(socket_base::broadcast(true));
boost::system::error_code error;
int numBytes = receive_from(socket, buffer(udpBuf), remoteEndpoint, error, timeout);
if (error && error != error::message_size && error != error::timed_out)
{
printf("Got error: %s\n", error.message().c_str());
return -1;
}
return numBytes;
}
/*
* The boost asio library does not provide a blocking read with timeout function so we have to roll our own.
*/
int receive_from(
boost::asio::ip::udp::socket& socket,
const boost::asio::mutable_buffers_1& buf,
boost::asio::ip::udp::endpoint& remoteEndpoint,
boost::system::error_code& error,
chrono::milliseconds timeout)
{
volatile bool ioDone = false;
int numBytesReceived = 0;
boost::asio::io_service& ioService = socket.get_io_service();
socket.async_receive_from(buf, remoteEndpoint,
[&error, &ioDone, &numBytesReceived](const boost::system::error_code& errorAsync, size_t bytesReceived)
{
ioDone = true;
error = errorAsync;
numBytesReceived = bytesReceived;
});
this_thread::sleep_for(chrono::milliseconds(100));
ioService.reset();
ioService.poll_one();
auto endTime = chrono::system_clock::now() + timeout;
while (!ioDone)
{
ioService.reset();
this_thread::sleep_for(chrono::milliseconds(100));
auto now = chrono::system_clock::now();
if (now > endTime)
{
socket.cancel();
error = error::timed_out;
return 0;
}
ioService.poll_one();
}
ioService.reset();
return numBytesReceived;
}
I checked similar questions on Stack Overflow and found some that said the receiving socket has to be bound to INADDR_ANY in order to receive broadcast packets. I was originally creating the socket like this udp::socket socket(ioService, udp::endpoint(udp::v4(), portNum)); but have now changed it to use ip::address_v4::any() instead. That didn't make any difference.
Can anybody tell me what I need to change in order to receive the UDP broadcast packet as expected ?
I am running this on iMX6 devices running Linux and am compiling using boost 1.58.
I finally discovered why my code was never seeing the broadcast packets even though tcpdump proved that they were being received. After finding this StackOverflow question:
Disable reverse path filtering from Linux kernel space
it seems that I just needed to disable Reverse Path Filtering on both hosts like this:
echo 0 > /proc/sys/net/ipv4/conf/all/rp_filter
echo 0 > /proc/sys/net/ipv4/conf/eth0/rp_filter
and then my code worked perfectly with no modifications. Hopefully this will help other people wondering why they can't get UDP broadcasts to the network broadcast address (255.255.255.255) to work.
I wrote a program that join source specific multicast group and receive udp multicast packets:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netdb.h>
#include <unistd.h>
#include <net/if.h>
typedef unsigned int UINT32;
int join_ssm_group(int s, UINT32 group, UINT32 source, UINT32 inter) {
struct ip_mreq_source imr;
imr.imr_multiaddr.s_addr = group;
imr.imr_sourceaddr.s_addr = source;
imr.imr_interface.s_addr = inter;
return setsockopt(s, IPPROTO_IP, IP_ADD_SOURCE_MEMBERSHIP, (char *) &imr, sizeof(imr));
}
UINT32 LISTEN_INTERFACE = inet_addr("10.10.1.2");
int main(int argc, char *argv[]) {
if (argc<3) {
printf(" Use: %s <group> <source> <port>", argv[0]);
return 1;
}
// Make socket
int sd = socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);
struct sockaddr_in Sender;
socklen_t SenderAddrSize = sizeof( Sender );
struct sockaddr_in binda;
// Bind it to listen appropriate UDP port
binda.sin_family = AF_INET;
binda.sin_port = htons( atoi(argv[3]));
= INADDR_ANY;
// binda.sin_addr.s_addr = LISTEN_INTERFACE;
bind(sd,(struct sockaddr*)&binda, sizeof(binda));
// Join to group
join_ssm_group( sd, inet_addr(argv[1]),
inet_addr(argv[2]),
INADDR_ANY );
char buf[65536];
UINT32 seq;
while(1) {
printf("try receive\n");
int res=recvfrom(sd,(char*)buf,sizeof(buf),0, (struct sockaddr *)& Sender, &SenderAddrSize);
printf("received\n");
seq = *(UINT32*)buf;
printf("scr=:%12s;\tseq=%6d;\tlen=%4d\n", inet_ntoa(Sender.sin_addr), seq, res);
}
return 0;
}
It works fine but note that I'm using binda.sin_addr.s_addr = INADDR_ANY;. netstat shows this:
netstat -a | grep 16002
udp 0 0 0.0.0.0:16002 0.0.0.0:*
When I change it to binda.sin_addr.s_addr = LISTEN_INTERFACE; program stops working - it can not recieve packets, it hangs in recvfrom. netstat shows this:
netstat -a | grep 16002
udp 0 0 localhost.localdo:16002 0.0.0.0:*
In both cases tcpdump shows that data is online, so the problem is that I can not receive data on the specific interface, only on ALL interfaces. I'm using RHEL 7, teaming, and LISTEN_INTERFACE is the IP of the corresponding VLAN. Why my code doesn't work and how to troubleshoot it? I do not want to use INADDR_ANY for performance reasons - listening ALL interfaces would be more expensive than listeining certain interface.
upd passing LISTEN_INTERFACE to both join_ssm_group and and binda.sin_addr.s_addr doesn't work too. BTW similar Windows version of such code works on the same PC under Windows Server 2008 R2, but it doesn't work in RHEL 7. I guess I should check these:
if RHEL 7 receives data on the requreid interface on the required port (answer is Yes, proved by tcpdump)
if socket is listening on the required interface on the required port (answer is Yes, proved by netstat?)
if both answers above are Yes then how is it possible that call to recvfrom doesn't receive data?
Well probably this question more about RHEL 7 now, than about c++.
When you join the multicast group you need to specify the same interface that you are listening on, or join it via all interfaces in a loop.
However listening on all interfaces is the norm. It is not 'slow', and it is a 'good idea', unless you have a specific reason to restrict who can connect.
I'm currently working with a Laser Sensor that delivers a UDP data stream on Port 2368. I can see the packets with Wireshark.
As I'm not able to post an image, I write what Wireshark shows for a packet:
Source: 192.168.17.141
Destination: 192.168.3.255
Protocol: UDP
Source Port: https (443)
Destination Port: opentable (2368)
However, I want to read the packets using sockets with following example C program:
int main(int argc, char *argv[])
{
int sock, n, res;
unsigned int length = 1206;
char* buffer = new char[1206];
sock= socket(AF_INET, SOCK_DGRAM, 0);
if (sock < 0) error("socket");
uint16_t udp_port = 2368;
sockaddr_in my_addr;
socklen_t len = sizeof(sockaddr_in);
memset(&my_addr, 0, sizeof(my_addr));
my_addr.sin_family = AF_INET;
my_addr.sin_port = htons(udp_port);
my_addr.sin_addr.s_addr = INADDR_ANY;
cout << my_addr.sin_family << endl;
cout << my_addr.sin_port << endl;
cout << my_addr.sin_addr.s_addr << endl;
res = bind(sock, (sockaddr *)&my_addr, sizeof(sockaddr_in));
if (res == -1)
{
perror("bind");
return -1;
}
while (true)
{
n = recvfrom(sock,buffer,1,0,NULL,NULL);
if (n < 0) error("recvfrom");
}
close(sock);
return 0;
}
The program is successful until it comes to recvfrom(). There the socket waits for packages and does not receive anything. I wrote the same program for Windows with Winsock and it worked perfectly. As I am relatively new to Linux OS I do not know how to fix this problem and would be thankful for advice!
Additional information: I manually assigned following IP and netmask to eth4 (this is the interface where the device is connected):
IP: 192.168.3.5
NM: 255.255.255.0
Set the SO_BROADCAST option, even for receiving. According to the socket(7) manpage:
SO_BROADCAST:
Set or get the broadcast flag. When enabled, datagram sockets receive packets sent to a broadcast address and they are allowed to send packets to a broadcast address. This option has no effect on stream-oriented sockets.
It could also be that your interface config is incorrect. Verify that you have a 192.168.3.xxx/24 address configured for the interface in question.
char buffer[1200+6]; /* no need for dynamic buffers */
...
n = recvfrom(sock, buffer, sizeof buffer, 0, NULL, NULL);
BTW your usage of recvfrom() is equivalent to
n = recv(sock, buffer, sizeof buffer, 0);
, or even:
n = read(sock, buffer, sizeof buffer);
You have
IP: 192.168.3.5 NM: 255.255.255.0
and
Source: 192.168.17.141 Destination: 192.168.3.255
This can't work, if there is no router involved. Try
IP: 192.168.3.5 NM: 255.255.0.0
as an interims measure, but do read up on IP
Edit: Maybe better look into your Laser sensor and set it to 192.168.3.[something free] with Destination directly your 192.168.3.5, and debug the broadcasting later.